Merge branch 'master' into oauth_login
authorDavid A. Velasco <dvelasco@solidgear.es>
Thu, 17 Jan 2013 13:25:49 +0000 (14:25 +0100)
committerDavid A. Velasco <dvelasco@solidgear.es>
Thu, 17 Jan 2013 13:25:49 +0000 (14:25 +0100)
Conflicts:
AndroidManifest.xml
res/values-de-rDE/strings.xml
res/values-de/strings.xml
res/values/strings.xml
src/com/owncloud/android/Uploader.java
src/com/owncloud/android/datamodel/FileDataStorageManager.java
src/com/owncloud/android/files/OwnCloudFileObserver.java
src/com/owncloud/android/files/services/FileDownloader.java
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/operations/RemoteOperationResult.java
src/com/owncloud/android/syncadapter/FileSyncAdapter.java
src/com/owncloud/android/ui/activity/AuthenticatorActivity.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/activity/UploadFilesActivity.java
src/com/owncloud/android/ui/fragment/FileDetailFragment.java
src/com/owncloud/android/ui/fragment/OCFileListFragment.java

1  2 
AndroidManifest.xml
res/values/strings.xml
src/com/owncloud/android/Uploader.java
src/com/owncloud/android/files/OwnCloudFileObserver.java
src/com/owncloud/android/files/services/FileDownloader.java
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/operations/RemoteOperationResult.java
src/com/owncloud/android/syncadapter/FileSyncAdapter.java
src/com/owncloud/android/ui/activity/AuthenticatorActivity.java
src/com/owncloud/android/ui/fragment/FileDetailFragment.java

diff --combined AndroidManifest.xml
@@@ -17,8 -17,8 +17,8 @@@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
   -->\r
  <manifest package="com.owncloud.android"\r
-     android:versionCode="103017"\r
-     android:versionName="1.3.17" xmlns:android="http://schemas.android.com/apk/res/android">\r
+     android:versionCode="103018"\r
+     android:versionName="1.3.18" xmlns:android="http://schemas.android.com/apk/res/android">\r
  \r
      <uses-permission android:name="android.permission.GET_ACCOUNTS" />\r
      <uses-permission android:name="android.permission.USE_CREDENTIALS" />\r
@@@ -88,7 -88,7 +88,7 @@@
          <service\r
              android:name=".authenticator.AccountAuthenticatorService"\r
              android:exported="true">\r
-             <intent-filter>\r
+             <intent-filter  android:priority="100">\r
                  <action android:name="android.accounts.AccountAuthenticator" />\r
              </intent-filter>\r
  \r
          <activity\r
              android:name=".ui.activity.AuthenticatorActivity"\r
              android:exported="true"\r
 -            android:theme="@style/Theme.ownCloud.noActionBar" >\r
 +            android:theme="@style/Theme.ownCloud.noActionBar" \r
 +            android:launchMode="singleTask">\r
 +            <intent-filter>\r
 +                <action android:name="android.intent.action.VIEW" />\r
 +\r
 +                <category android:name="android.intent.category.DEFAULT" />\r
 +                <category android:name="android.intent.category.BROWSABLE" />\r
 +\r
 +                <data android:scheme="oauth-mobile-app" />\r
 +            </intent-filter>\r
          </activity>\r
  \r
          <service android:name=".files.services.FileDownloader" >\r
          
          <service android:name=".files.services.FileUploader" >\r
          </service>
 -        <service android:name=".files.services.InstantUploadService" />
          <receiver android:name=".files.InstantUploadBroadcastReceiver">\r
              <intent-filter>\r
                  <action android:name="com.android.camera.NEW_PICTURE" />\r
                  <action android:name="android.intent.action.BOOT_COMPLETED"/>\r
              </intent-filter>\r
          </receiver>\r
 -        <service android:name=".files.services.FileObserverService"/>
 +        <service android:name=".files.services.FileObserverService"/>\r
 +        \r
 +        <service android:name=".authenticator.oauth2.services.OAuth2GetTokenService" >\r
 +        </service>\r
 +        
      </application>\r
  \r
  </manifest>
diff --combined res/values/strings.xml
      <string name="sync_fail_in_favourites_ticker">Kept-in-sync files failed</string>
        <string name="sync_fail_in_favourites_content">Contents of %1$d files could not be sync\'ed (%2$d conflicts)</string>
        <string name="sync_foreign_files_forgotten_ticker">Some local files were forgotten</string>
-       <string name="sync_foreign_files_forgotten_content">%1$d files out of the ownCloud directory could not be copied into</string>
+       <string name="sync_foreign_files_forgotten_content">%1$d files out of the %2$s directory could not be copied into</string>
        <string name="sync_foreign_files_forgotten_explanation">"As of version 1.3.16, files uploaded from this device are copied into the local %1$s folder to prevent data loss when a single file is synced with multiple accounts.\n\nDue to this change, all files uploaded in previous versions of this app were copied into the %2$s folder. However, an error prevented the completion of this operation during account synchronization. You may either leave the file(s) as is and remove the link to %3$s, or move the file(s) into the %1$s directory and retain the link to %4$s.\n\nListed below are the local file(s), and the the remote file(s) in %5$s they were linked to.</string>
      
      <string name="foreign_files_move">"Move all"</string>
      <string name="filedisplay_unexpected_bad_get_content">"Unexpected problem ; please select the file from a different app"</string>
      <string name="filedisplay_no_file_selected">No file was selected</string>
      
 +    <string name="oauth_host_url">oAuth2 URL</string> 
 +    <string name="oauth_check_onoff">Login with oAuth2.</string> 
 +    <string name="oauth_login_connection">Connecting to oAuth2 server…</string>    
 +    <string name="oauth_code_validation_message">Please, open a web browser and go to:\n%1$s.\nValidate this code there:\n%2$s</string>
 +    <string name="oauth_connection_url_unavailable">Connection to this URL not available.</string> 
 +        
      <string name="ssl_validator_title">Warning</string>
      <string name="ssl_validator_header">The identity of the site could not be verified</string>
      <string name="ssl_validator_reason_cert_not_trusted">- The server certificate is not trusted</string>
@@@ -30,7 -30,7 +30,6 @@@ import com.owncloud.android.datamodel.D
  import com.owncloud.android.datamodel.FileDataStorageManager;\r
  import com.owncloud.android.datamodel.OCFile;\r
  import com.owncloud.android.files.services.FileUploader;\r
--import com.owncloud.android.network.OwnCloudClientUtils;\r
  \r
  import android.accounts.Account;\r
  import android.accounts.AccountManager;\r
@@@ -60,7 -60,7 +59,6 @@@ import android.widget.SimpleAdapter
  import android.widget.Toast;\r
  \r
  import com.owncloud.android.R;\r
--import eu.alefzero.webdav.WebdavClient;\r
  \r
  /**\r
   * This can be used to upload things to an ownCloud instance.\r
@@@ -325,58 -325,24 +323,24 @@@ public class Uploader extends ListActiv
          mFile = mStorageManager.getFileByPath(full_path);\r
          if (mFile != null) {\r
              Vector<OCFile> files = mStorageManager.getDirectoryContent(mFile);\r
-             if (files.size() > 0) {\r
-                 List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();\r
-                 for (OCFile f : files) {\r
-                     HashMap<String, Object> h = new HashMap<String, Object>();\r
-                     if (f.isDirectory()) {\r
-                         h.put("dirname", f.getFileName());\r
-                         data.add(h);\r
-                     }\r
+             List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();\r
+             for (OCFile f : files) {\r
+                 HashMap<String, Object> h = new HashMap<String, Object>();\r
+                 if (f.isDirectory()) {\r
+                     h.put("dirname", f.getFileName());\r
+                     data.add(h);\r
                  }\r
-                 SimpleAdapter sa = new SimpleAdapter(this,\r
-                                                      data,\r
-                                                      R.layout.uploader_list_item_layout,\r
-                                                      new String[] {"dirname"},\r
-                                                      new int[] {R.id.textView1});\r
-                 setListAdapter(sa);\r
-                 Button btn = (Button) findViewById(R.id.uploader_choose_folder);\r
-                 btn.setOnClickListener(this);\r
-                 getListView().setOnItemClickListener(this);\r
              }\r
-         }\r
-         /*\r
-         mCursor = managedQuery(ProviderMeta.ProviderTableMeta.CONTENT_URI, null, ProviderTableMeta.FILE_NAME\r
-                 + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?", new String[] { "/", mAccount.name }, null);\r
\r
-         if (mCursor.moveToFirst()) {\r
-             mCursor = managedQuery(\r
-                     ProviderMeta.ProviderTableMeta.CONTENT_URI,\r
-                     null,\r
-                     ProviderTableMeta.FILE_CONTENT_TYPE + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND "\r
-                             + ProviderTableMeta.FILE_PARENT + "=?",\r
-                     new String[] { "DIR", mAccount.name,\r
-                             mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta._ID)) }, null);\r
\r
-             ListView lv = getListView();\r
-             lv.setOnItemClickListener(this);\r
-             SimpleCursorAdapter sca = new SimpleCursorAdapter(this, R.layout.uploader_list_item_layout, mCursor,\r
-                     new String[] { ProviderTableMeta.FILE_NAME }, new int[] { R.id.textView1 });\r
-             setListAdapter(sca);\r
+             SimpleAdapter sa = new SimpleAdapter(this,\r
+                                                 data,\r
+                                                 R.layout.uploader_list_item_layout,\r
+                                                 new String[] {"dirname"},\r
+                                                 new int[] {R.id.textView1});\r
+             setListAdapter(sa);\r
              Button btn = (Button) findViewById(R.id.uploader_choose_folder);\r
              btn.setOnClickListener(this);\r
-             /*\r
-              * disable this until new server interaction service wont be created\r
-              * // insert create new directory for multiple items uploading if\r
-              * (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {\r
-              * Button createDirBtn = new Button(this);\r
-              * createDirBtn.setId(android.R.id.button1);\r
-              * createDirBtn.setText(R.string.uploader_btn_create_dir_text);\r
-              * createDirBtn.setOnClickListener(this); ((LinearLayout)\r
-              * findViewById(R.id.linearLayout1)).addView( createDirBtn,\r
-              * LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); }\r
-              *\r
-         }*/\r
+             getListView().setOnItemClickListener(this);\r
+         }\r
      }\r
  \r
      private boolean prepareStreamsToUpload() {\r
  \r
      public void uploadFiles() {\r
          try {\r
 +            /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed \r
              WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());\r
 -\r
              // create last directory in path if necessary\r
              if (mCreateDir) {\r
                  wdc.createDirectory(mUploadPath);\r
              }\r
 +            */\r
  \r
              String[] local = new String[mStreamsToUpload.size()], remote = new String[mStreamsToUpload.size()];\r
  \r
@@@ -22,13 -22,13 +22,11 @@@ import java.io.File
  
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
--import com.owncloud.android.network.OwnCloudClientUtils;
  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 eu.alefzero.webdav.WebdavClient;
  
  import android.accounts.Account;
  import android.content.Context;
@@@ -77,6 -77,7 +75,6 @@@ public class OwnCloudFileObserver exten
                           mPath);
              return;
          }
 -        WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mOCAccount, mContext);
          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; 
@@@ -87,7 -88,7 +85,7 @@@
                                                                      true, 
                                                                      true, 
                                                                      mContext);
 -        RemoteOperationResult result = sfo.execute(wc);
 +        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);
@@@ -19,7 -19,6 +19,7 @@@
  package com.owncloud.android.files.services;\r
  \r
  import java.io.File;\r
 +import java.io.IOException;\r
  import java.util.AbstractList;\r
  import java.util.Iterator;\r
  import java.util.Vector;\r
@@@ -37,7 -36,6 +37,7 @@@ import com.owncloud.android.ui.activity
  import com.owncloud.android.ui.fragment.FileDetailFragment;\r
  \r
  import android.accounts.Account;\r
 +import android.accounts.AccountsException;\r
  import android.app.Notification;\r
  import android.app.NotificationManager;\r
  import android.app.PendingIntent;\r
@@@ -200,6 -198,7 +200,7 @@@ public class FileDownloader extends Ser
           * @param file          A file that could be in the queue of downloads.\r
           */\r
          public boolean isDownloading(Account account, OCFile file) {\r
+             if (account == null || file == null) return false;\r
              String targetKey = buildRemoteName(account, file);\r
              synchronized (mPendingDownloads) {\r
                  if (file.isDirectory()) {\r
              \r
              notifyDownloadStart(mCurrentDownload);\r
  \r
 -            /// prepare client object to send the request to the ownCloud server\r
 -            if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {\r
 -                mLastAccount = mCurrentDownload.getAccount();\r
 -                mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());\r
 -                mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());\r
 -            }\r
 -\r
 -            /// perform the download\r
              RemoteOperationResult downloadResult = null;\r
              try {\r
 -                downloadResult = mCurrentDownload.execute(mDownloadClient);\r
 +                /// prepare client object to send the request to the ownCloud server\r
 +                if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {\r
 +                    mLastAccount = mCurrentDownload.getAccount();\r
 +                    mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());\r
-                     mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, this);\r
++                    mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());\r
 +                }\r
 +\r
 +                /// perform the download\r
 +                if (downloadResult == null) {\r
 +                    downloadResult = mCurrentDownload.execute(mDownloadClient);\r
 +                }\r
                  if (downloadResult.isSuccess()) {\r
                      saveDownloadedFile();\r
                  }\r
              \r
 +            } catch (AccountsException e) {\r
 +                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);\r
 +                downloadResult = new RemoteOperationResult(e);\r
 +            } catch (IOException e) {\r
 +                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);\r
 +                downloadResult = new RemoteOperationResult(e);\r
 +                \r
              } finally {\r
                  synchronized(mPendingDownloads) {\r
                      mPendingDownloads.remove(downloadKey);\r
@@@ -19,7 -19,6 +19,7 @@@
  package com.owncloud.android.files.services;
  
  import java.io.File;
 +import java.io.IOException;
  import java.util.AbstractList;
  import java.util.Iterator;
  import java.util.Vector;
@@@ -35,8 -34,6 +35,8 @@@ import com.owncloud.android.datamodel.F
  import com.owncloud.android.datamodel.OCFile;
  import com.owncloud.android.files.InstantUploadBroadcastReceiver;
  import com.owncloud.android.operations.ChunkedUploadFileOperation;
 +import com.owncloud.android.operations.CreateFolderOperation;
 +import com.owncloud.android.operations.RemoteOperation;
  import com.owncloud.android.operations.RemoteOperationResult;
  import com.owncloud.android.operations.UploadFileOperation;
  import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
@@@ -52,7 -49,6 +52,7 @@@ import com.owncloud.android.network.Own
  
  import android.accounts.Account;
  import android.accounts.AccountManager;
 +import android.accounts.AccountsException;
  import android.app.Notification;
  import android.app.NotificationManager;
  import android.app.PendingIntent;
@@@ -328,6 -324,7 +328,7 @@@ public class FileUploader extends Servi
           * @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()) {
              
              notifyUploadStart(mCurrentUpload);
  
 +            RemoteOperationResult uploadResult = null;
              
 -            /// 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());
 -            }
 +            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());
 +                }
              
 -            /// create remote folder for instant uploads
 -            if (mCurrentUpload.isRemoteFolderToBeCreated()) {
 -                mUploadClient.createDirectory(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR);    // ignoring result; fail could just mean that it already exists, but local database is not synchronized; the upload will be tried anyway
 -            }
 +                /// create remote folder for instant uploads
 +                if (mCurrentUpload.isRemoteFolderToBeCreated()) {
 +                    RemoteOperation operation = new CreateFolderOperation(  InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR, 
 +                                                                            mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR).getFileId(), // TODO generalize this : INSTANT_UPLOAD_DIR could not be a child of root
 +                                                                            mStorageManager);
 +                    operation.execute(mUploadClient);      // ignoring result; fail could just mean that it already exists, but local database is not synchronized; the upload will be tried anyway
 +                }
  
              
 -            /// perform the upload
 -            RemoteOperationResult uploadResult = null;
 -            try {
 +                /// perform the upload
                  uploadResult = mCurrentUpload.execute(mUploadClient);
                  if (uploadResult.isSuccess()) {
                      saveUploadedFile();
                  }
                  
 +            } catch (AccountsException e) {
 +                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
 +                uploadResult = new RemoteOperationResult(e);
 +                
 +            } catch (IOException e) {
 +                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
 +                uploadResult = new RemoteOperationResult(e);
 +                
              } finally {
                  synchronized(mPendingUploads) {
                      mPendingUploads.remove(uploadKey);
                  }
              }
 -        
 +            
              /// notify result
              notifyUploadResult(uploadResult, mCurrentUpload);
              
@@@ -45,7 -45,8 +45,7 @@@ import com.owncloud.android.network.Cer
  public class RemoteOperationResult implements Serializable {
      
      /** Generated - should be refreshed every time the class changes!! */
-     private static final long serialVersionUID = 5336333154035462033L;
+     private static final long serialVersionUID = -7805531062432602444L;
 -
      
      public enum ResultCode { 
          OK,
@@@ -68,7 -69,6 +68,7 @@@
          INVALID_LOCAL_FILE_NAME, 
          INVALID_OVERWRITE,
          CONFLICT, 
 +        OAUTH2_ERROR,
          SYNC_CONFLICT,
          LOCAL_STORAGE_FULL, 
          LOCAL_STORAGE_NOT_MOVED, 
@@@ -79,7 -79,6 +79,6 @@@
      private int mHttpCode = -1;
      private Exception mException = null;
      private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
-     private Object mExtraData = null;
      
      public RemoteOperationResult(ResultCode code) {
          mCode = code;
          return mCode == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
      }
      
-     public void setExtraData(Object data) {
-         mExtraData = data;
-     }
-     
-     public Object getExtraData() {
-         return mExtraData;
-     }
-     
      private CertificateCombinedException getCertificateCombinedException(Exception e) {
          CertificateCombinedException result = null;
          if (e instanceof CertificateCombinedException) {
@@@ -37,9 -37,6 +37,9 @@@ import com.owncloud.android.operations.
  import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
  import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;\r
  import android.accounts.Account;\r
 +import android.accounts.AccountsException;\r
 +import android.accounts.AuthenticatorException;\r
 +import android.accounts.OperationCanceledException;\r
  import android.app.Notification;\r
  import android.app.NotificationManager;\r
  import android.app.PendingIntent;\r
@@@ -105,12 -102,7 +105,12 @@@ public class FileSyncAdapter extends Ab
          this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));\r
          try {\r
              this.initClientForCurrentAccount();\r
 -        } catch (UnknownHostException e) {\r
 +        } catch (IOException e) {\r
 +            /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again\r
 +            mSyncResult.tooManyRetries = true;\r
 +            notifyFailedSynchronization();\r
 +            return;\r
 +        } catch (AccountsException e) {\r
              /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again\r
              mSyncResult.tooManyRetries = true;\r
              notifyFailedSynchronization();\r
          notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), explanationIntent, 0);\r
          notification.setLatestEventInfo(getContext().getApplicationContext(), \r
                                          getContext().getString(R.string.sync_foreign_files_forgotten_ticker), \r
-                                         String.format(getContext().getString(R.string.sync_foreign_files_forgotten_content), mForgottenLocalFiles.size()), \r
+                                         String.format(getContext().getString(R.string.sync_foreign_files_forgotten_content), mForgottenLocalFiles.size(), getContext().getString(R.string.app_name)), \r
                                          notification.contentIntent);\r
          ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_foreign_files_forgotten_ticker, notification);\r
          \r
@@@ -20,29 -20,16 +20,29 @@@ package com.owncloud.android.ui.activit
  \r
  import java.net.MalformedURLException;\r
  import java.net.URL;\r
 +import java.util.HashMap;\r
 +import java.util.Map;\r
 +\r
 +import org.json.JSONException;\r
 +import org.json.JSONObject;\r
  \r
  import com.owncloud.android.AccountUtils;\r
  import com.owncloud.android.authenticator.AccountAuthenticator;\r
  import com.owncloud.android.authenticator.AuthenticationRunnable;\r
  import com.owncloud.android.authenticator.OnAuthenticationResultListener;\r
  import com.owncloud.android.authenticator.OnConnectCheckListener;\r
 +import com.owncloud.android.authenticator.oauth2.OAuth2Context;\r
 +import com.owncloud.android.authenticator.oauth2.OAuth2GetCodeRunnable;\r
 +import com.owncloud.android.authenticator.oauth2.OnOAuth2GetCodeResultListener;\r
 +import com.owncloud.android.authenticator.oauth2.connection.ConnectorOAuth2;\r
 +import com.owncloud.android.authenticator.oauth2.services.OAuth2GetTokenService;\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
  import com.owncloud.android.network.OwnCloudClientUtils;\r
  import com.owncloud.android.operations.ConnectionCheckOperation;\r
 +import com.owncloud.android.operations.ExistenceCheckOperation;\r
 +import com.owncloud.android.operations.GetOAuth2AccessToken;\r
  import com.owncloud.android.operations.OnRemoteOperationListener;\r
  import com.owncloud.android.operations.RemoteOperation;\r
  import com.owncloud.android.operations.RemoteOperationResult;\r
@@@ -53,12 -40,9 +53,12 @@@ import android.accounts.AccountManager
  import android.app.AlertDialog;\r
  import android.app.Dialog;\r
  import android.app.ProgressDialog;\r
 +import android.content.BroadcastReceiver;\r
  import android.content.ContentResolver;\r
 +import android.content.Context;\r
  import android.content.DialogInterface;\r
  import android.content.Intent;\r
 +import android.content.IntentFilter;\r
  import android.content.SharedPreferences;\r
  import android.net.Uri;\r
  import android.os.Bundle;\r
@@@ -70,9 -54,8 +70,9 @@@ import android.view.View
  import android.view.View.OnClickListener;\r
  import android.view.View.OnFocusChangeListener;\r
  import android.view.Window;\r
 -import android.widget.Button;\r
 +import android.widget.CheckBox;\r
  import android.widget.EditText;\r
 +import android.widget.Button;\r
  import android.widget.ImageView;\r
  import android.widget.TextView;\r
  import com.owncloud.android.R;\r
@@@ -87,7 -70,7 +87,7 @@@ import eu.alefzero.webdav.WebdavClient
   */\r
  public class AuthenticatorActivity extends AccountAuthenticatorActivity\r
          implements OnAuthenticationResultListener, OnConnectCheckListener, OnRemoteOperationListener, OnSslValidatorListener, \r
 -        OnFocusChangeListener, OnClickListener {\r
 +        OnFocusChangeListener, OnClickListener, OnOAuth2GetCodeResultListener {\r
  \r
      private static final int DIALOG_LOGIN_PROGRESS = 0;\r
      private static final int DIALOG_SSL_VALIDATOR = 1;\r
  \r
      private Thread mAuthThread;\r
      private AuthenticationRunnable mAuthRunnable;\r
 -    //private ConnectionCheckerRunnable mConnChkRunnable = null;\r
      private ConnectionCheckOperation mConnChkRunnable;\r
 +    private ExistenceCheckOperation mAuthChkOperation;\r
      private final Handler mHandler = new Handler();\r
      private String mBaseUrl;\r
 +    private OwnCloudVersion mDiscoveredVersion;\r
      \r
      private static final String STATUS_TEXT = "STATUS_TEXT";\r
      private static final String STATUS_ICON = "STATUS_ICON";\r
      private static final String STATUS_CORRECT = "STATUS_CORRECT";\r
      private static final String IS_SSL_CONN = "IS_SSL_CONN";\r
 +    private static final String OC_VERSION = "OC_VERSION";\r
      private int mStatusText, mStatusIcon;\r
      private boolean mStatusCorrect, mIsSslConn;\r
      private RemoteOperationResult mLastSslUntrustedServerResult;\r
  \r
 +    public static final String PARAM_ACCOUNTNAME = "param_Accountname";\r
 +    \r
      public static final String PARAM_USERNAME = "param_Username";\r
      public static final String PARAM_HOSTNAME = "param_Hostname";\r
  \r
 +    // oAuth2 variables.\r
 +    private static final int OAUTH2_LOGIN_PROGRESS = 3;\r
 +    private static final String OAUTH2_STATUS_TEXT = "OAUTH2_STATUS_TEXT";\r
 +    private static final String OAUTH2_STATUS_ICON = "OAUTH2_STATUS_ICON";\r
 +    private static final String OAUTH2_CODE_RESULT = "CODE_RESULT";\r
 +    private static final String OAUTH2_IS_CHECKED = "OAUTH2_IS_CHECKED";    \r
 +    private Thread mOAuth2GetCodeThread;\r
 +    private OAuth2GetCodeRunnable mOAuth2GetCodeRunnable;     \r
 +    private TokenReceiver tokenReceiver;\r
 +    private JSONObject codeResponseJson; \r
 +    private int mOAuth2StatusText, mOAuth2StatusIcon;    \r
 +    \r
 +    public ConnectorOAuth2 connectorOAuth2;\r
 +    \r
 +    // Variables used to save the on the state the contents of all fields.\r
 +    private static final String HOST_URL_TEXT = "HOST_URL_TEXT";\r
 +    private static final String ACCOUNT_USERNAME = "ACCOUNT_USERNAME";\r
 +    private static final String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD";\r
 +    \r
 +    //private boolean mNewRedirectUriCaptured;\r
 +    private Uri mNewCapturedUriFromOAuth2Redirection;\r
 +\r
 +    // END of oAuth2 variables.\r
 +    \r
      @Override\r
      protected void onCreate(Bundle savedInstanceState) {\r
          super.onCreate(savedInstanceState);\r
          ImageView iv2 = (ImageView) findViewById(R.id.viewPassword);\r
          TextView tv = (TextView) findViewById(R.id.host_URL);\r
          TextView tv2 = (TextView) findViewById(R.id.account_password);\r
 +        EditText oauth2Url = (EditText)findViewById(R.id.oAuth_URL);\r
 +        oauth2Url.setText("OWNCLOUD AUTHORIZATION PROVIDER IN TEST");\r
  \r
          if (savedInstanceState != null) {\r
              mStatusIcon = savedInstanceState.getInt(STATUS_ICON);\r
              if (!mStatusCorrect)\r
                  iv.setVisibility(View.VISIBLE);\r
              else\r
 -                iv.setVisibility(View.INVISIBLE);\r
 +                iv.setVisibility(View.INVISIBLE);        \r
 +            \r
 +            String ocVersion = savedInstanceState.getString(OC_VERSION, null);\r
 +            if (ocVersion != null)\r
 +                mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
 +            \r
 +            // Getting the state of oAuth2 components.\r
 +            mOAuth2StatusIcon = savedInstanceState.getInt(OAUTH2_STATUS_ICON);\r
 +            mOAuth2StatusText = savedInstanceState.getInt(OAUTH2_STATUS_TEXT);\r
 +                // We set this to true if the rotation happens when the user is validating oAuth2 user_code.\r
 +            changeViewByOAuth2Check(savedInstanceState.getBoolean(OAUTH2_IS_CHECKED));\r
 +                // We store a JSon object with all the data returned from oAuth2 server when we get user_code.\r
 +                // Is better than store variable by variable. We use String object to serialize from/to it.\r
 +            try {\r
 +                if (savedInstanceState.containsKey(OAUTH2_CODE_RESULT)) {\r
 +                    codeResponseJson = new JSONObject(savedInstanceState.getString(OAUTH2_CODE_RESULT));\r
 +                }\r
 +            } catch (JSONException e) {\r
 +                Log.e(TAG, "onCreate->JSONException: " + e.toString());\r
 +            }\r
 +            // END of getting the state of oAuth2 components.\r
 +            \r
 +            // Getting contents of each field.\r
 +            EditText hostUrl = (EditText)findViewById(R.id.host_URL);\r
 +            hostUrl.setText(savedInstanceState.getString(HOST_URL_TEXT), TextView.BufferType.EDITABLE);\r
 +            EditText accountUsername = (EditText)findViewById(R.id.account_username);\r
 +            accountUsername.setText(savedInstanceState.getString(ACCOUNT_USERNAME), TextView.BufferType.EDITABLE);\r
 +            EditText accountPassword = (EditText)findViewById(R.id.account_password);\r
 +            accountPassword.setText(savedInstanceState.getString(ACCOUNT_PASSWORD), TextView.BufferType.EDITABLE);\r
 +            // END of getting contents of each field\r
  \r
          } else {\r
              mStatusText = mStatusIcon = 0;\r
              mStatusCorrect = false;\r
              mIsSslConn = false;\r
 +            \r
 +            String accountName = getIntent().getExtras().getString(PARAM_ACCOUNTNAME);\r
 +            String tokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
 +            if (AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(tokenType)) {\r
 +                CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
 +                oAuth2Check.setChecked(true);\r
 +                changeViewByOAuth2Check(true);\r
 +            } \r
 +            \r
 +            if (accountName != null) {\r
 +                ((TextView) findViewById(R.id.account_username)).setText(accountName.substring(0, accountName.lastIndexOf('@')));\r
 +                tv.setText(accountName.substring(accountName.lastIndexOf('@') + 1));\r
 +            }\r
          }\r
          iv.setOnClickListener(this);\r
          iv2.setOnClickListener(this);\r
          tv.setOnFocusChangeListener(this);\r
          tv2.setOnFocusChangeListener(this);\r
 -\r
 +        \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
 +        mNewCapturedUriFromOAuth2Redirection = null;\r
      }\r
  \r
 +    \r
 +    @Override\r
 +    protected void onNewIntent (Intent intent) {\r
 +        Uri data = intent.getData();\r
 +        //mNewRedirectUriCaptured = (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI));\r
 +        if (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI)) {\r
 +            mNewCapturedUriFromOAuth2Redirection = data;\r
 +        }\r
 +        Log.d(TAG, "onNewIntent()");\r
 +    \r
 +    }\r
 +    \r
 +    \r
      @Override\r
      protected void onSaveInstanceState(Bundle outState) {\r
          outState.putInt(STATUS_ICON, mStatusIcon);\r
          outState.putInt(STATUS_TEXT, mStatusText);\r
          outState.putBoolean(STATUS_CORRECT, mStatusCorrect);\r
 +        if (mDiscoveredVersion != null) \r
 +            outState.putString(OC_VERSION, mDiscoveredVersion.toString());\r
 +        \r
 +        // Saving the state of oAuth2 components.\r
 +        outState.putInt(OAUTH2_STATUS_ICON, mOAuth2StatusIcon);\r
 +        outState.putInt(OAUTH2_STATUS_TEXT, mOAuth2StatusText);\r
 +        CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
 +        outState.putBoolean(OAUTH2_IS_CHECKED, oAuth2Check.isChecked());\r
 +        if (codeResponseJson != null){\r
 +            outState.putString(OAUTH2_CODE_RESULT, codeResponseJson.toString());\r
 +        }\r
 +        // END of saving the state of oAuth2 components.\r
 +        \r
 +        // Saving contents of each field.\r
 +        outState.putString(HOST_URL_TEXT,((TextView) findViewById(R.id.host_URL)).getText().toString().trim());\r
 +        outState.putString(ACCOUNT_USERNAME,((TextView) findViewById(R.id.account_username)).getText().toString().trim());\r
 +        outState.putString(ACCOUNT_PASSWORD,((TextView) findViewById(R.id.account_password)).getText().toString().trim());\r
 +        \r
          super.onSaveInstanceState(outState);\r
      }\r
  \r
              dialog = working_dialog;\r
              break;\r
          }\r
 +        // oAuth2 dialog. We show here to the user the URL and user_code that the user must validate in a web browser.\r
 +        case OAUTH2_LOGIN_PROGRESS: {\r
 +            ProgressDialog working_dialog = new ProgressDialog(this);\r
 +            try {\r
 +                if (codeResponseJson != null && codeResponseJson.has(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL)) {\r
 +                    working_dialog.setMessage(String.format(getString(R.string.oauth_code_validation_message), \r
 +                            codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL), \r
 +                            codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE)));\r
 +                } else {\r
 +                    working_dialog.setMessage(String.format("Getting authorization"));\r
 +                }\r
 +            } catch (JSONException e) {\r
 +                Log.e(TAG, "onCreateDialog->JSONException: " + e.toString());\r
 +            }\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.i(TAG, "Login canceled");\r
 +                    if (mOAuth2GetCodeThread != null) {\r
 +                        mOAuth2GetCodeThread.interrupt();\r
 +                        finish();\r
 +                    } \r
 +                    if (tokenReceiver != null) {\r
 +                        unregisterReceiver(tokenReceiver);\r
 +                        tokenReceiver = null;\r
 +                        finish();\r
 +                    }\r
 +                }\r
 +            });\r
 +            dialog = working_dialog;\r
 +            break;\r
 +        }\r
          case DIALOG_SSL_VALIDATOR: {\r
              dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);\r
              break;\r
          switch (id) {\r
          case DIALOG_LOGIN_PROGRESS:\r
          case DIALOG_CERT_NOT_SAVED:\r
 +        case OAUTH2_LOGIN_PROGRESS:\r
              break;\r
          case DIALOG_SSL_VALIDATOR: {\r
              ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);\r
              Log.e(TAG, "Incorrect dialog called with id = " + id);\r
          }\r
      }\r
 +    \r
 +    @Override\r
 +    protected void onResume() {\r
 +        Log.d(TAG, "onResume() start");\r
 +        // (old oauth code) Registering token receiver. We must listening to the service that is pooling to the oAuth server for a token.\r
 +        if (tokenReceiver == null) {\r
 +            IntentFilter tokenFilter = new IntentFilter(OAuth2GetTokenService.TOKEN_RECEIVED_MESSAGE);                \r
 +            tokenReceiver = new TokenReceiver();\r
 +            this.registerReceiver(tokenReceiver,tokenFilter);\r
 +        }\r
 +        // (new oauth code)\r
 +        /*if (mNewRedirectUriCaptured) {\r
 +            mNewRedirectUriCaptured = false;*/\r
 +        if (mNewCapturedUriFromOAuth2Redirection != null) {\r
 +            getOAuth2AccessTokenFromCapturedRedirection();            \r
 +            \r
 +        }\r
 +        super.onResume();\r
 +    }\r
 +\r
 +    @Override\r
 +    protected void onPause() {\r
 +        Log.d(TAG, "onPause() start");\r
 +        super.onPause();\r
 +    }    \r
 +    \r
  \r
      public void onAuthenticationResult(boolean success, String message) {\r
          if (success) {\r
                      AccountAuthenticator.ACCOUNT_TYPE);\r
              intent.putExtra(AccountManager.KEY_USERDATA, username);\r
  \r
 +            accManager.setUserData(account, AccountAuthenticator.KEY_OC_URL,\r
 +                    url.toString());\r
              accManager.setUserData(account,\r
 -                    AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable\r
 -                            .getDiscoveredVersion().toString());\r
 +                    AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString());\r
              \r
              accManager.setUserData(account,\r
                      AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);\r
                  || url.toLowerCase().startsWith("https://")) {\r
              prefix = "";\r
          }\r
 -        continueConnection(prefix);\r
 +        CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
 +        if (oAuth2Check != null && oAuth2Check.isChecked()) {\r
 +            startOauthorization();\r
 +            \r
 +        } else {\r
 +            continueConnection(prefix);\r
 +        }\r
      }\r
      \r
 +    private void startOauthorization() {\r
 +        // We start a thread to get an authorization code from the oAuth2 server.\r
 +        setOAuth2ResultIconAndText(R.drawable.progress_small, R.string.oauth_login_connection);\r
 +        mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_F_AUTHORIZATION_ENDPOINT_URL, this);\r
 +        //mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_G_DEVICE_GETCODE_URL, this);\r
 +        mOAuth2GetCodeRunnable.setListener(this, mHandler);\r
 +        mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable);\r
 +        mOAuth2GetCodeThread.start();\r
 +    }\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
              url = url.substring(0, url.length() - 1);\r
  \r
          URL uri = null;\r
 -        String webdav_path = AccountUtils.getWebdavPath(mConnChkRunnable\r
 -                .getDiscoveredVersion());\r
 +        mDiscoveredVersion = mConnChkRunnable.getDiscoveredVersion();\r
 +        String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false);\r
          \r
          if (webdav_path == null) {\r
              onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));\r
          findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);\r
      }\r
  \r
 -    @Override\r
      public void onFocusChange(View view, boolean hasFocus) {\r
          if (view.getId() == R.id.host_URL) {\r
              if (!hasFocus) {\r
                      setResultIconAndText(R.drawable.progress_small,\r
                              R.string.auth_testing_connection);\r
                      //mConnChkRunnable = new ConnectionCheckerRunnable(uri, this);\r
 -                    mConnChkRunnable = new ConnectionCheckOperation(uri, this);\r
 +                    mConnChkRunnable = new  ConnectionCheckOperation(uri, this);\r
                      //mConnChkRunnable.setListener(this, mHandler);\r
                      //mAuthThread = new Thread(mConnChkRunnable);\r
                      //mAuthThread.start();\r
                        WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this);\r
 +                      mDiscoveredVersion = null;\r
                      mAuthThread = mConnChkRunnable.execute(client, this, mHandler);\r
                  } else {\r
                      findViewById(R.id.refreshButton).setVisibility(\r
          if (v.getId() == R.id.refreshButton) {\r
              onFocusChange(findViewById(R.id.host_URL), false);\r
          } else if (v.getId() == R.id.viewPassword) {\r
-             TextView view = (TextView) findViewById(R.id.account_password);\r
+             EditText view = (EditText) findViewById(R.id.account_password);\r
+             int selectionStart = view.getSelectionStart();\r
+             int selectionEnd = view.getSelectionEnd();\r
              int input_type = view.getInputType();\r
              if ((input_type & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {\r
                  input_type = InputType.TYPE_CLASS_TEXT\r
                          | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;\r
              }\r
              view.setInputType(input_type);\r
+             view.setSelection(selectionStart, selectionEnd);\r
          }\r
      }\r
 +    \r
 +    @Override protected void onDestroy() {       \r
 +        // We must stop the service thats it's pooling to oAuth2 server for a token.\r
 +        Intent tokenService = new Intent(this, OAuth2GetTokenService.class);\r
 +        stopService(tokenService);\r
 +        \r
 +        // We stop listening the result of the pooling service.\r
 +        if (tokenReceiver != null) {\r
 +            unregisterReceiver(tokenReceiver);\r
 +            tokenReceiver = null;\r
 +            finish();\r
 +        }\r
 +\r
 +        super.onDestroy();\r
 +    }    \r
 +    \r
 +    // Controlling the oAuth2 checkbox on the activity: hide and show widgets.\r
 +    public void onOff_check_Click(View view) {\r
 +        CheckBox oAuth2Check = (CheckBox)view;      \r
 +        changeViewByOAuth2Check(oAuth2Check.isChecked());\r
 +\r
 +    }\r
 +    \r
 +    public void changeViewByOAuth2Check(Boolean checked) {\r
 +        \r
 +        EditText oAuth2Url = (EditText) findViewById(R.id.oAuth_URL);\r
 +        EditText accountUsername = (EditText) findViewById(R.id.account_username);\r
 +        EditText accountPassword = (EditText) findViewById(R.id.account_password);\r
 +        ImageView viewPassword = (ImageView) findViewById(R.id.viewPassword); \r
 +        ImageView auth2ActionIndicator = (ImageView) findViewById(R.id.auth2_action_indicator); \r
 +        TextView oauth2StatusText = (TextView) findViewById(R.id.oauth2_status_text);         \r
 +\r
 +        if (checked) {\r
 +            oAuth2Url.setVisibility(View.VISIBLE);\r
 +            accountUsername.setVisibility(View.GONE);\r
 +            accountPassword.setVisibility(View.GONE);\r
 +            viewPassword.setVisibility(View.GONE);\r
 +            auth2ActionIndicator.setVisibility(View.INVISIBLE);\r
 +            oauth2StatusText.setVisibility(View.INVISIBLE);\r
 +        } else {\r
 +            oAuth2Url.setVisibility(View.GONE);\r
 +            accountUsername.setVisibility(View.VISIBLE);\r
 +            accountPassword.setVisibility(View.VISIBLE);\r
 +            viewPassword.setVisibility(View.INVISIBLE);\r
 +            auth2ActionIndicator.setVisibility(View.GONE);\r
 +            oauth2StatusText.setVisibility(View.GONE);\r
 +        }     \r
 +\r
 +    }    \r
 +    \r
 +    // Controlling the oAuth2 result of server connection.\r
 +    private void setOAuth2ResultIconAndText(int drawable_id, int text_id) {\r
 +        ImageView iv = (ImageView) findViewById(R.id.auth2_action_indicator);\r
 +        TextView tv = (TextView) findViewById(R.id.oauth2_status_text);\r
 +\r
 +        if (drawable_id == 0 && text_id == 0) {\r
 +            iv.setVisibility(View.INVISIBLE);\r
 +            tv.setVisibility(View.INVISIBLE);\r
 +        } else {\r
 +            iv.setImageResource(drawable_id);\r
 +            tv.setText(text_id);\r
 +            iv.setVisibility(View.VISIBLE);\r
 +            tv.setVisibility(View.VISIBLE);\r
 +        }\r
 +    }     \r
 +    \r
 +    // Results from the first call to oAuth2 server : getting the user_code and verification_url.\r
 +    @Override\r
 +    public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject responseJson) {\r
 +        if ((type == ResultOAuthType.OK_SSL)||(type == ResultOAuthType.OK_NO_SSL)) {\r
 +            codeResponseJson = responseJson;\r
 +            if (codeResponseJson != null) {\r
 +                getOAuth2AccessTokenFromJsonResponse();\r
 +            }  // else - nothing to do here - wait for callback !!!\r
 +        \r
 +        } else if (type == ResultOAuthType.HOST_NOT_AVAILABLE) {\r
 +            setOAuth2ResultIconAndText(R.drawable.common_error, R.string.oauth_connection_url_unavailable);\r
 +        }\r
 +    }\r
 +\r
 +    // If the results of getting the user_code and verification_url are OK, we get the received data and we start\r
 +    // the polling service to oAuth2 server to get a valid token.\r
 +    private void getOAuth2AccessTokenFromJsonResponse() {\r
 +        String deviceCode = null;\r
 +        String verificationUrl = null;\r
 +        String userCode = null;\r
 +        int expiresIn = -1;\r
 +        int interval = -1;\r
 +\r
 +        Log.d(TAG, "ResponseOAuth2->" + codeResponseJson.toString());\r
 +\r
 +        try {\r
 +            // We get data that we must show to the user or we will use internally.\r
 +            verificationUrl = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL);\r
 +            userCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE);\r
 +            expiresIn = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_EXPIRES_IN);                \r
 +\r
 +            // And we get data that we must use to get a token.\r
 +            deviceCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_DEVICE_CODE);\r
 +            interval = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_INTERVAL);\r
 +\r
 +        } catch (JSONException e) {\r
 +            Log.e(TAG, "Exception accesing data in Json object" + e.toString());\r
 +        }\r
 +\r
 +        // Updating status widget to OK.\r
 +        setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established);\r
 +        \r
 +        // Showing the dialog with instructions for the user.\r
 +        showDialog(OAUTH2_LOGIN_PROGRESS);\r
 +\r
 +        // Loggin all the data.\r
 +        Log.d(TAG, "verificationUrl->" + verificationUrl);\r
 +        Log.d(TAG, "userCode->" + userCode);\r
 +        Log.d(TAG, "deviceCode->" + deviceCode);\r
 +        Log.d(TAG, "expiresIn->" + expiresIn);\r
 +        Log.d(TAG, "interval->" + interval);\r
 +\r
 +        // Starting the pooling service.\r
 +        try {\r
 +            Intent tokenService = new Intent(this, OAuth2GetTokenService.class);\r
 +            tokenService.putExtra(OAuth2GetTokenService.TOKEN_URI, OAuth2Context.OAUTH2_G_DEVICE_GETTOKEN_URL);\r
 +            tokenService.putExtra(OAuth2GetTokenService.TOKEN_DEVICE_CODE, deviceCode);\r
 +            tokenService.putExtra(OAuth2GetTokenService.TOKEN_INTERVAL, interval);\r
 +\r
 +            startService(tokenService);\r
 +        }\r
 +        catch (Exception e) {\r
 +            Log.e(TAG, "tokenService creation problem :", e);\r
 +        }\r
 +        \r
 +    }   \r
 +    \r
 +    private void getOAuth2AccessTokenFromCapturedRedirection() {\r
 +        Map<String, String> responseValues = new HashMap<String, String>();\r
 +        //String queryParameters = getIntent().getData().getQuery();\r
 +        String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();\r
 +        mNewCapturedUriFromOAuth2Redirection = null;\r
 +        \r
 +        Log.v(TAG, "Queryparameters (Code) = " + queryParameters);\r
 +\r
 +        String[] pairs = queryParameters.split("&");\r
 +        Log.v(TAG, "Pairs (Code) = " + pairs.toString());\r
 +\r
 +        int i = 0;\r
 +        String key = "";\r
 +        String value = "";\r
 +\r
 +        StringBuilder sb = new StringBuilder();\r
 +\r
 +        while (pairs.length > i) {\r
 +            int j = 0;\r
 +            String[] part = pairs[i].split("=");\r
 +\r
 +            while (part.length > j) {\r
 +                String p = part[j];\r
 +                if (j == 0) {\r
 +                    key = p;\r
 +                    sb.append(key + " = ");\r
 +                } else if (j == 1) {\r
 +                    value = p;\r
 +                    responseValues.put(key, value);\r
 +                    sb.append(value + "\n");\r
 +                }\r
 +\r
 +                Log.v(TAG, "[" + i + "," + j + "] = " + p);\r
 +                j++;\r
 +            }\r
 +            i++;\r
 +        }\r
 +        \r
 +        \r
 +        // Updating status widget to OK.\r
 +        setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established);\r
 +        \r
 +        // Showing the dialog with instructions for the user.\r
 +        showDialog(OAUTH2_LOGIN_PROGRESS);\r
 +\r
 +        // \r
 +        RemoteOperation operation = new GetOAuth2AccessToken(responseValues);\r
 +        WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(OAuth2Context.OAUTH2_F_TOKEN_ENDPOINT_URL), getApplicationContext());\r
 +        operation.execute(client, this, mHandler);\r
 +    }\r
 +\r
 +    \r
 +\r
 +    // We get data from the oAuth2 token service with this broadcast receiver.\r
 +    private class TokenReceiver extends BroadcastReceiver {\r
 +        /**\r
 +         * The token is received.\r
 +         *  @author\r
 +         * {@link BroadcastReceiver} to enable oAuth2 token receiving.\r
 +         */\r
 +        @Override\r
 +        public void onReceive(Context context, Intent intent) {\r
 +            @SuppressWarnings("unchecked")\r
 +            HashMap<String, String> tokenResponse = (HashMap<String, String>)intent.getExtras().get(OAuth2GetTokenService.TOKEN_RECEIVED_DATA);\r
 +            Log.d(TAG, "TokenReceiver->" + tokenResponse.get(OAuth2GetTokenService.TOKEN_ACCESS_TOKEN));\r
 +            dismissDialog(OAUTH2_LOGIN_PROGRESS);\r
 +\r
 +        }\r
 +    }\r
  \r
        @Override\r
        public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
 -              if (operation.equals(mConnChkRunnable)) {\r
 +              if (operation instanceof ConnectionCheckOperation) {\r
                    \r
                mStatusText = mStatusIcon = 0;\r
                mStatusCorrect = false;\r
                else\r
                    findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);\r
                findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);\r
 +              \r
 +              } else if (operation instanceof GetOAuth2AccessToken) {\r
 +\r
 +            try {\r
 +                dismissDialog(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
 +                  if (result.isSuccess()) {\r
 +                      \r
 +                      /// time to test the retrieved access token on the ownCloud server\r
 +                      String url = ((TextView) findViewById(R.id.host_URL)).getText()\r
 +                              .toString().trim();\r
 +                      if (url.endsWith("/"))\r
 +                          url = url.substring(0, url.length() - 1);\r
 +\r
 +                      Uri uri = null;\r
 +                      /*String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion);\r
 +                      \r
 +                      if (webdav_path == null) {\r
 +                          onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));\r
 +                          return;\r
 +                      }*/\r
 +                      \r
 +                      String prefix = "";\r
 +                      if (mIsSslConn) {\r
 +                          prefix = "https://";\r
 +                      } else {\r
 +                          prefix = "http://";\r
 +                      }\r
 +                      if (url.toLowerCase().startsWith("http://")\r
 +                              || url.toLowerCase().startsWith("https://")) {\r
 +                          prefix = "";\r
 +                      }\r
 +                      \r
 +                      try {\r
 +                          mBaseUrl = prefix + url;\r
 +                          //String url_str = prefix + url + webdav_path;\r
 +                          String url_str = prefix + url + "/remote.php/odav";\r
 +                          uri = Uri.parse(url_str);\r
 +                          \r
 +                      } catch (Exception e) {\r
 +                          // should never happen\r
 +                          onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title));\r
 +                          return;\r
 +                      }\r
 +\r
 +                      showDialog(DIALOG_LOGIN_PROGRESS);\r
 +                String accessToken = ((GetOAuth2AccessToken)operation).getResultTokenMap().get(OAuth2Context.KEY_ACCESS_TOKEN);\r
 +                Log.d(TAG, "Got ACCESS TOKEN: " + accessToken);\r
 +                      mAuthChkOperation = new ExistenceCheckOperation("", this, accessToken);\r
 +                      WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(uri, getApplicationContext());\r
 +                      mAuthChkOperation.execute(client, this, mHandler);\r
 +                      \r
 +                \r
 +            } else {\r
 +                TextView tv = (TextView) findViewById(R.id.oAuth_URL);\r
 +                tv.setError("A valid authorization could not be obtained");\r
 +\r
 +            }\r
 +                      \r
 +              } else if (operation instanceof ExistenceCheckOperation)  {\r
 +                      \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
 +                TextView tv = (TextView) findViewById(R.id.oAuth_URL);\r
 +                      Log.d(TAG, "Checked access - time to save the account");\r
 +                      \r
 +                      Uri uri = Uri.parse(mBaseUrl);\r
 +                      String username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong(); \r
 +                      String accountName = username + "@" + uri.getHost();\r
 +                      if (uri.getPort() >= 0) {\r
 +                          accountName += ":" + uri.getPort();\r
 +                      }\r
 +                      // TODO - check that accountName does not exist\r
 +                      Account account = new Account(accountName, AccountAuthenticator.ACCOUNT_TYPE);\r
 +                      AccountManager accManager = AccountManager.get(this);\r
 +                      accManager.addAccountExplicitly(account, "", null);  // with our implementation, the password is never input in the app\r
 +\r
 +                      // Add this account as default in the preferences, if there is none\r
 +                      Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);\r
 +                      if (defaultAccount == null) {\r
 +                          SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();\r
 +                          editor.putString("select_oc_account", accountName);\r
 +                      editor.commit();\r
 +                      }\r
 +\r
 +                      /// account data to save by the AccountManager\r
 +                      final Intent intent = new Intent();\r
 +                      intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE);\r
 +                      intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);\r
 +                      intent.putExtra(AccountManager.KEY_USERDATA, username);\r
 +\r
 +                accManager.setAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, ((ExistenceCheckOperation) operation).getAccessToken());\r
 +                \r
 +                      accManager.setUserData(account, AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable.getDiscoveredVersion().toString());\r
 +                      accManager.setUserData(account, AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);\r
 +                      accManager.setUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE");\r
 +\r
 +                      setAccountAuthenticatorResult(intent.getExtras());\r
 +                      setResult(RESULT_OK, intent);\r
 +                      \r
 +                      /// enforce the first account synchronization\r
 +                      Bundle bundle = new Bundle();\r
 +                      bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
 +                      ContentResolver.requestSync(account, "org.owncloud", bundle);\r
 +\r
 +                      finish();\r
 +                      \r
 +                  } else {      \r
 +                      TextView tv = (TextView) findViewById(R.id.oAuth_URL);\r
 +                      tv.setError(result.getLogMessage());\r
 +                Log.d(TAG, "Access failed: " + result.getLogMessage());\r
 +                  }\r
                }\r
        }\r
  \r
      public void onFailedSavingCertificate() {\r
          showDialog(DIALOG_CERT_NOT_SAVED);\r
      }\r
 -    \r
 +\r
  }\r
  package com.owncloud.android.ui.fragment;\r
  \r
  import java.io.File;\r
 -import java.util.ArrayList;\r
 -import java.util.List;\r
 -\r
 -import org.apache.commons.httpclient.methods.GetMethod;\r
 -import org.apache.commons.httpclient.methods.PostMethod;\r
 -import org.apache.commons.httpclient.methods.StringRequestEntity;\r
 -import org.apache.commons.httpclient.params.HttpConnectionManagerParams;\r
 -import org.apache.http.HttpStatus;\r
 -import org.apache.http.NameValuePair;\r
 -import org.apache.http.client.utils.URLEncodedUtils;\r
 -import org.apache.http.message.BasicNameValuePair;\r
 -import org.apache.http.protocol.HTTP;\r
 -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;\r
 -import org.json.JSONObject;\r
 +\r
 +import org.apache.commons.httpclient.Credentials;\r
- import org.apache.commons.httpclient.UsernamePasswordCredentials;\r
  \r
  import android.accounts.Account;\r
  import android.accounts.AccountManager;\r
@@@ -55,6 -66,7 +54,6 @@@ import android.widget.TextView
  import android.widget.Toast;\r
  \r
  import com.actionbarsherlock.app.SherlockFragment;\r
 -import com.owncloud.android.AccountUtils;\r
  import com.owncloud.android.DisplayUtils;\r
  import com.owncloud.android.authenticator.AccountAuthenticator;\r
  import com.owncloud.android.datamodel.FileDataStorageManager;\r
@@@ -64,8 -76,7 +63,7 @@@ import com.owncloud.android.files.servi
  import com.owncloud.android.files.services.FileUploader;\r
  import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
  import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
 -import com.owncloud.android.network.OwnCloudClientUtils;\r
 +import com.owncloud.android.network.BearerCredentials;\r
- import com.owncloud.android.network.OwnCloudClientUtils;\r
  import com.owncloud.android.operations.OnRemoteOperationListener;\r
  import com.owncloud.android.operations.RemoteOperation;\r
  import com.owncloud.android.operations.RemoteOperationResult;\r
@@@ -79,8 -90,10 +77,8 @@@ import com.owncloud.android.ui.activity
  import com.owncloud.android.ui.activity.TransferServiceGetter;\r
  import com.owncloud.android.ui.dialog.EditNameDialog;\r
  import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;\r
 -import com.owncloud.android.utils.OwnCloudVersion;\r
  \r
  import com.owncloud.android.R;\r
 -import eu.alefzero.webdav.WebdavClient;\r
  import eu.alefzero.webdav.WebdavUtils;\r
  \r
  /**\r
@@@ -142,10 -155,6 +140,6 @@@ public class FileDetailFragment extend
          mAccount = ocAccount;\r
          mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment \r
          mLayout = R.layout.file_details_empty;\r
-         \r
-         if(fileToDetail != null && ocAccount != null) {\r
-             mLayout = R.layout.file_details_fragment;\r
-         }\r
      }\r
      \r
      \r
              mAccount = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_ACCOUNT);\r
          }\r
          \r
+         if(mFile != null && mAccount != null) {\r
+             mLayout = R.layout.file_details_fragment;\r
+         }\r
+         \r
          View view = null;\r
          view = inflater.inflate(mLayout, container, false);\r
          mView = view;\r
                      \r
                  } else {\r
                      mLastRemoteOperation = new SynchronizeFileOperation(mFile, null, mStorageManager, mAccount, true, false, getActivity());\r
 -                    WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
 -                    mLastRemoteOperation.execute(wc, this, mHandler);\r
 +                    mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());\r
                  \r
                      // update ui \r
                      boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
                  mLastRemoteOperation = new RemoveFileOperation( mFile, \r
                                                                  true, \r
                                                                  mStorageManager);\r
 -                WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
 -                mLastRemoteOperation.execute(wc, this, mHandler);\r
 -                \r
 +                mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());\r
-                 \r
                  boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
                  getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
              }\r
       * @return  True when the fragment was created with the empty layout.\r
       */\r
      public boolean isEmpty() {\r
-         return mLayout == R.layout.file_details_empty;\r
+         return (mLayout == R.layout.file_details_empty || mFile == null || mAccount == null);\r
      }\r
  \r
      \r
                  if (mFile.getRemotePath().equals(uploadRemotePath) ||\r
                      renamedInUpload) {\r
                      if (uploadWasFine) {\r
-                         mFile = mStorageManager.getFileByPath(mFile.getRemotePath());\r
 -                        mFile = mStorageManager.getFileByPath(uploadRemotePath);\r
++                       mFile = mStorageManager.getFileByPath(uploadRemotePath);\r
                      }\r
                      if (renamedInUpload) {\r
                          String newName = (new File(uploadRemotePath)).getName();\r
                          Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG);\r
                          msg.show();\r
-                         getSherlockActivity().removeStickyBroadcast(intent);    // not the best place to do this; a small refactorization of BroadcastReceivers should be done\r
                      }\r
+                     getSherlockActivity().removeStickyBroadcast(intent);    // not the best place to do this; a small refactorization of BroadcastReceivers should be done\r
                      updateFileDetails(false);    // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server\r
                  }\r
              }\r
      \r
  \r
      // this is a temporary class for sharing purposes, it need to be replaced in transfer service\r
 +    /*\r
      @SuppressWarnings("unused")\r
      private class ShareRunnable implements Runnable {\r
          private String mPath;\r
              }\r
          }\r
      }\r
 +    */\r
      \r
      public void onDismiss(EditNameDialog dialog) {\r
          if (dialog.getResult()) {\r
                                                              mAccount, \r
                                                              newFilename, \r
                                                              new FileDataStorageManager(mAccount, getActivity().getContentResolver()));\r
 -            WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
 -            mLastRemoteOperation.execute(wc, this, mHandler);\r
 +            mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());\r
              boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
              getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
          }\r
       */\r
      @Override\r
      public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
 -        if (operation.equals(mLastRemoteOperation)) {\r
 +        if (!result.isSuccess() && result.getCode() == ResultCode.UNAUTHORIZED) {\r
 +            AccountManager am = AccountManager.get(getSherlockActivity());\r
 +            //am.invalidateAuthToken(AccountAuthenticator.ACCOUNT_TYPE, OwnCloudClientUtils.getAuthorizationTokenType(operation.getClient().getCredentials()));\r
 +            Credentials cred = operation.getClient().getCredentials();\r
 +            if (cred instanceof BearerCredentials) {\r
 +                am.invalidateAuthToken(AccountAuthenticator.ACCOUNT_TYPE, ((BearerCredentials)cred).getAccessToken());\r
 +            } else {\r
 +                am.clearPassword(mAccount);\r
 +            }\r
 +            operation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());  // need a new client instance, so avoid retry()\r
 +                // TODO si el usuario no se autoriza de nuevo, esto genera un bucle infinito; o un error en la creación del objecto cliente\r
 +            \r
 +        } else {\r
              if (operation instanceof RemoveFileOperation) {\r
                  onRemoveFileOperationFinish((RemoveFileOperation)operation, result);\r
                  \r