From: David A. Velasco Date: Thu, 17 Jan 2013 13:25:49 +0000 (+0100) Subject: Merge branch 'master' into oauth_login X-Git-Tag: oc-android-1.4.3~29^2~22 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/2b5785556e7f7c3cefc5f084949e091f80abed6d?hp=-c Merge branch 'master' into oauth_login 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 --- 2b5785556e7f7c3cefc5f084949e091f80abed6d diff --combined AndroidManifest.xml index 613095bd,994efb76..3cb25781 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@@ -17,8 -17,8 +17,8 @@@ along with this program. If not, see . --> + android:versionCode="103018" + android:versionName="1.3.18" xmlns:android="http://schemas.android.com/apk/res/android"> @@@ -88,7 -88,7 +88,7 @@@ - + @@@ -120,16 -120,7 +120,16 @@@ + android:theme="@style/Theme.ownCloud.noActionBar" + android:launchMode="singleTask"> + + + + + + + + @@@ -146,6 -137,7 +146,6 @@@ - @@@ -160,11 -152,7 +160,11 @@@ - + + + + + diff --combined res/values/strings.xml index 5b1ca77d,0041d1b8..5afdd86e --- a/res/values/strings.xml +++ b/res/values/strings.xml @@@ -116,7 -116,7 +116,7 @@@ Kept-in-sync files failed Contents of %1$d files could not be sync\'ed (%2$d conflicts) Some local files were forgotten - %1$d files out of the ownCloud directory could not be copied into + %1$d files out of the %2$s directory could not be copied into "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. "Move all" @@@ -215,12 -215,6 +215,12 @@@ "Unexpected problem ; please select the file from a different app" No file was selected + oAuth2 URL + Login with oAuth2. + Connecting to oAuth2 server… + Please, open a web browser and go to:\n%1$s.\nValidate this code there:\n%2$s + Connection to this URL not available. + Warning The identity of the site could not be verified - The server certificate is not trusted diff --combined src/com/owncloud/android/Uploader.java index 0db4871d,e19a9724..6ec8c4fd --- a/src/com/owncloud/android/Uploader.java +++ b/src/com/owncloud/android/Uploader.java @@@ -30,7 -30,7 +30,6 @@@ import com.owncloud.android.datamodel.D import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileUploader; --import com.owncloud.android.network.OwnCloudClientUtils; import android.accounts.Account; import android.accounts.AccountManager; @@@ -60,7 -60,7 +59,6 @@@ import android.widget.SimpleAdapter import android.widget.Toast; import com.owncloud.android.R; --import eu.alefzero.webdav.WebdavClient; /** * This can be used to upload things to an ownCloud instance. @@@ -325,58 -325,24 +323,24 @@@ public class Uploader extends ListActiv mFile = mStorageManager.getFileByPath(full_path); if (mFile != null) { Vector files = mStorageManager.getDirectoryContent(mFile); - if (files.size() > 0) { - List> data = new LinkedList>(); - for (OCFile f : files) { - HashMap h = new HashMap(); - if (f.isDirectory()) { - h.put("dirname", f.getFileName()); - data.add(h); - } + List> data = new LinkedList>(); + for (OCFile f : files) { + HashMap h = new HashMap(); + if (f.isDirectory()) { + h.put("dirname", f.getFileName()); + data.add(h); } - SimpleAdapter sa = new SimpleAdapter(this, - data, - R.layout.uploader_list_item_layout, - new String[] {"dirname"}, - new int[] {R.id.textView1}); - setListAdapter(sa); - Button btn = (Button) findViewById(R.id.uploader_choose_folder); - btn.setOnClickListener(this); - getListView().setOnItemClickListener(this); } - } - /* - mCursor = managedQuery(ProviderMeta.ProviderTableMeta.CONTENT_URI, null, ProviderTableMeta.FILE_NAME - + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?", new String[] { "/", mAccount.name }, null); - - if (mCursor.moveToFirst()) { - mCursor = managedQuery( - ProviderMeta.ProviderTableMeta.CONTENT_URI, - null, - ProviderTableMeta.FILE_CONTENT_TYPE + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " - + ProviderTableMeta.FILE_PARENT + "=?", - new String[] { "DIR", mAccount.name, - mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta._ID)) }, null); - - ListView lv = getListView(); - lv.setOnItemClickListener(this); - SimpleCursorAdapter sca = new SimpleCursorAdapter(this, R.layout.uploader_list_item_layout, mCursor, - new String[] { ProviderTableMeta.FILE_NAME }, new int[] { R.id.textView1 }); - setListAdapter(sca); + SimpleAdapter sa = new SimpleAdapter(this, + data, + R.layout.uploader_list_item_layout, + new String[] {"dirname"}, + new int[] {R.id.textView1}); + setListAdapter(sa); Button btn = (Button) findViewById(R.id.uploader_choose_folder); btn.setOnClickListener(this); - /* - * disable this until new server interaction service wont be created - * // insert create new directory for multiple items uploading if - * (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) { - * Button createDirBtn = new Button(this); - * createDirBtn.setId(android.R.id.button1); - * createDirBtn.setText(R.string.uploader_btn_create_dir_text); - * createDirBtn.setOnClickListener(this); ((LinearLayout) - * findViewById(R.id.linearLayout1)).addView( createDirBtn, - * LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); } - * - }*/ + getListView().setOnItemClickListener(this); + } } private boolean prepareStreamsToUpload() { @@@ -391,13 -357,12 +355,13 @@@ public void uploadFiles() { try { + /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext()); - // create last directory in path if necessary if (mCreateDir) { wdc.createDirectory(mUploadPath); } + */ String[] local = new String[mStreamsToUpload.size()], remote = new String[mStreamsToUpload.size()]; diff --combined src/com/owncloud/android/files/OwnCloudFileObserver.java index 9dde939b,8a03fee6..d79a07e2 --- a/src/com/owncloud/android/files/OwnCloudFileObserver.java +++ b/src/com/owncloud/android/files/OwnCloudFileObserver.java @@@ -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); diff --combined src/com/owncloud/android/files/services/FileDownloader.java index 665290cf,7872c367..ed83d863 --- a/src/com/owncloud/android/files/services/FileDownloader.java +++ b/src/com/owncloud/android/files/services/FileDownloader.java @@@ -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; @@@ -37,7 -36,6 +37,7 @@@ import com.owncloud.android.ui.activity import com.owncloud.android.ui.fragment.FileDetailFragment; import android.accounts.Account; +import android.accounts.AccountsException; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@@ -200,6 -198,7 +200,7 @@@ public class FileDownloader extends Ser * @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()) { @@@ -264,30 -263,21 +265,30 @@@ notifyDownloadStart(mCurrentDownload); - /// prepare client object to send the request to the ownCloud server - if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) { - mLastAccount = mCurrentDownload.getAccount(); - mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver()); - mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext()); - } - - /// perform the download RemoteOperationResult downloadResult = null; try { - downloadResult = mCurrentDownload.execute(mDownloadClient); + /// 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, this); ++ mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext()); + } + + /// perform the download + if (downloadResult == null) { + downloadResult = mCurrentDownload.execute(mDownloadClient); + } if (downloadResult.isSuccess()) { saveDownloadedFile(); } + } catch (AccountsException e) { + Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e); + downloadResult = new RemoteOperationResult(e); + } catch (IOException e) { + Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e); + downloadResult = new RemoteOperationResult(e); + } finally { synchronized(mPendingDownloads) { mPendingDownloads.remove(downloadKey); diff --combined src/com/owncloud/android/files/services/FileUploader.java index 6b8fc855,0a033469..524f4c10 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@@ -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()) { @@@ -395,45 -392,34 +396,45 @@@ 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); diff --combined src/com/owncloud/android/operations/RemoteOperationResult.java index b26fc378,64703386..5a14fb87 --- a/src/com/owncloud/android/operations/RemoteOperationResult.java +++ b/src/com/owncloud/android/operations/RemoteOperationResult.java @@@ -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; @@@ -177,14 -176,6 +176,6 @@@ 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) { diff --combined src/com/owncloud/android/syncadapter/FileSyncAdapter.java index 1cc1303f,f66980cb..46c088b4 --- a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java @@@ -37,9 -37,6 +37,9 @@@ import com.owncloud.android.operations. import com.owncloud.android.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity; import android.accounts.Account; +import android.accounts.AccountsException; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@@ -105,12 -102,7 +105,12 @@@ public class FileSyncAdapter extends Ab this.setStorageManager(new FileDataStorageManager(account, getContentProvider())); try { this.initClientForCurrentAccount(); - } catch (UnknownHostException e) { + } 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(); @@@ -367,7 -359,7 +367,7 @@@ 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()), + 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 --combined src/com/owncloud/android/ui/activity/AuthenticatorActivity.java index fb5c711b,bd36ed1c..fc84df60 --- a/src/com/owncloud/android/ui/activity/AuthenticatorActivity.java +++ b/src/com/owncloud/android/ui/activity/AuthenticatorActivity.java @@@ -20,29 -20,16 +20,29 @@@ package com.owncloud.android.ui.activit import java.net.MalformedURLException; import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONException; +import org.json.JSONObject; import com.owncloud.android.AccountUtils; import com.owncloud.android.authenticator.AccountAuthenticator; import com.owncloud.android.authenticator.AuthenticationRunnable; import com.owncloud.android.authenticator.OnAuthenticationResultListener; import com.owncloud.android.authenticator.OnConnectCheckListener; +import com.owncloud.android.authenticator.oauth2.OAuth2Context; +import com.owncloud.android.authenticator.oauth2.OAuth2GetCodeRunnable; +import com.owncloud.android.authenticator.oauth2.OnOAuth2GetCodeResultListener; +import com.owncloud.android.authenticator.oauth2.connection.ConnectorOAuth2; +import com.owncloud.android.authenticator.oauth2.services.OAuth2GetTokenService; import com.owncloud.android.ui.dialog.SslValidatorDialog; import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener; +import com.owncloud.android.utils.OwnCloudVersion; import com.owncloud.android.network.OwnCloudClientUtils; import com.owncloud.android.operations.ConnectionCheckOperation; +import com.owncloud.android.operations.ExistenceCheckOperation; +import com.owncloud.android.operations.GetOAuth2AccessToken; import com.owncloud.android.operations.OnRemoteOperationListener; import com.owncloud.android.operations.RemoteOperation; import com.owncloud.android.operations.RemoteOperationResult; @@@ -53,12 -40,9 +53,12 @@@ import android.accounts.AccountManager import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; +import android.content.BroadcastReceiver; import android.content.ContentResolver; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; @@@ -70,9 -54,8 +70,9 @@@ import android.view.View import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.Window; -import android.widget.Button; +import android.widget.CheckBox; import android.widget.EditText; +import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import com.owncloud.android.R; @@@ -87,7 -70,7 +87,7 @@@ import eu.alefzero.webdav.WebdavClient */ public class AuthenticatorActivity extends AccountAuthenticatorActivity implements OnAuthenticationResultListener, OnConnectCheckListener, OnRemoteOperationListener, OnSslValidatorListener, - OnFocusChangeListener, OnClickListener { + OnFocusChangeListener, OnClickListener, OnOAuth2GetCodeResultListener { private static final int DIALOG_LOGIN_PROGRESS = 0; private static final int DIALOG_SSL_VALIDATOR = 1; @@@ -97,50 -80,22 +97,50 @@@ private Thread mAuthThread; private AuthenticationRunnable mAuthRunnable; - //private ConnectionCheckerRunnable mConnChkRunnable = null; private ConnectionCheckOperation mConnChkRunnable; + private ExistenceCheckOperation mAuthChkOperation; private final Handler mHandler = new Handler(); private String mBaseUrl; + private OwnCloudVersion mDiscoveredVersion; private static final String STATUS_TEXT = "STATUS_TEXT"; private static final String STATUS_ICON = "STATUS_ICON"; private static final String STATUS_CORRECT = "STATUS_CORRECT"; private static final String IS_SSL_CONN = "IS_SSL_CONN"; + private static final String OC_VERSION = "OC_VERSION"; private int mStatusText, mStatusIcon; private boolean mStatusCorrect, mIsSslConn; private RemoteOperationResult mLastSslUntrustedServerResult; + public static final String PARAM_ACCOUNTNAME = "param_Accountname"; + public static final String PARAM_USERNAME = "param_Username"; public static final String PARAM_HOSTNAME = "param_Hostname"; + // oAuth2 variables. + private static final int OAUTH2_LOGIN_PROGRESS = 3; + private static final String OAUTH2_STATUS_TEXT = "OAUTH2_STATUS_TEXT"; + private static final String OAUTH2_STATUS_ICON = "OAUTH2_STATUS_ICON"; + private static final String OAUTH2_CODE_RESULT = "CODE_RESULT"; + private static final String OAUTH2_IS_CHECKED = "OAUTH2_IS_CHECKED"; + private Thread mOAuth2GetCodeThread; + private OAuth2GetCodeRunnable mOAuth2GetCodeRunnable; + private TokenReceiver tokenReceiver; + private JSONObject codeResponseJson; + private int mOAuth2StatusText, mOAuth2StatusIcon; + + public ConnectorOAuth2 connectorOAuth2; + + // Variables used to save the on the state the contents of all fields. + private static final String HOST_URL_TEXT = "HOST_URL_TEXT"; + private static final String ACCOUNT_USERNAME = "ACCOUNT_USERNAME"; + private static final String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD"; + + //private boolean mNewRedirectUriCaptured; + private Uri mNewCapturedUriFromOAuth2Redirection; + + // END of oAuth2 variables. + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@@ -150,8 -105,6 +150,8 @@@ ImageView iv2 = (ImageView) findViewById(R.id.viewPassword); TextView tv = (TextView) findViewById(R.id.host_URL); TextView tv2 = (TextView) findViewById(R.id.account_password); + EditText oauth2Url = (EditText)findViewById(R.id.oAuth_URL); + oauth2Url.setText("OWNCLOUD AUTHORIZATION PROVIDER IN TEST"); if (savedInstanceState != null) { mStatusIcon = savedInstanceState.getInt(STATUS_ICON); @@@ -163,104 -116,29 +163,104 @@@ if (!mStatusCorrect) iv.setVisibility(View.VISIBLE); else - iv.setVisibility(View.INVISIBLE); + iv.setVisibility(View.INVISIBLE); + + String ocVersion = savedInstanceState.getString(OC_VERSION, null); + if (ocVersion != null) + mDiscoveredVersion = new OwnCloudVersion(ocVersion); + + // Getting the state of oAuth2 components. + mOAuth2StatusIcon = savedInstanceState.getInt(OAUTH2_STATUS_ICON); + mOAuth2StatusText = savedInstanceState.getInt(OAUTH2_STATUS_TEXT); + // We set this to true if the rotation happens when the user is validating oAuth2 user_code. + changeViewByOAuth2Check(savedInstanceState.getBoolean(OAUTH2_IS_CHECKED)); + // We store a JSon object with all the data returned from oAuth2 server when we get user_code. + // Is better than store variable by variable. We use String object to serialize from/to it. + try { + if (savedInstanceState.containsKey(OAUTH2_CODE_RESULT)) { + codeResponseJson = new JSONObject(savedInstanceState.getString(OAUTH2_CODE_RESULT)); + } + } catch (JSONException e) { + Log.e(TAG, "onCreate->JSONException: " + e.toString()); + } + // END of getting the state of oAuth2 components. + + // Getting contents of each field. + EditText hostUrl = (EditText)findViewById(R.id.host_URL); + hostUrl.setText(savedInstanceState.getString(HOST_URL_TEXT), TextView.BufferType.EDITABLE); + EditText accountUsername = (EditText)findViewById(R.id.account_username); + accountUsername.setText(savedInstanceState.getString(ACCOUNT_USERNAME), TextView.BufferType.EDITABLE); + EditText accountPassword = (EditText)findViewById(R.id.account_password); + accountPassword.setText(savedInstanceState.getString(ACCOUNT_PASSWORD), TextView.BufferType.EDITABLE); + // END of getting contents of each field } else { mStatusText = mStatusIcon = 0; mStatusCorrect = false; mIsSslConn = false; + + String accountName = getIntent().getExtras().getString(PARAM_ACCOUNTNAME); + String tokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE); + if (AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(tokenType)) { + CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check); + oAuth2Check.setChecked(true); + changeViewByOAuth2Check(true); + } + + if (accountName != null) { + ((TextView) findViewById(R.id.account_username)).setText(accountName.substring(0, accountName.lastIndexOf('@'))); + tv.setText(accountName.substring(accountName.lastIndexOf('@') + 1)); + } } iv.setOnClickListener(this); iv2.setOnClickListener(this); tv.setOnFocusChangeListener(this); tv2.setOnFocusChangeListener(this); - + Button b = (Button) findViewById(R.id.account_register); if (b != null) { b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name))); } + + mNewCapturedUriFromOAuth2Redirection = null; } + + @Override + protected void onNewIntent (Intent intent) { + Uri data = intent.getData(); + //mNewRedirectUriCaptured = (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI)); + if (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI)) { + mNewCapturedUriFromOAuth2Redirection = data; + } + Log.d(TAG, "onNewIntent()"); + + } + + @Override protected void onSaveInstanceState(Bundle outState) { outState.putInt(STATUS_ICON, mStatusIcon); outState.putInt(STATUS_TEXT, mStatusText); outState.putBoolean(STATUS_CORRECT, mStatusCorrect); + if (mDiscoveredVersion != null) + outState.putString(OC_VERSION, mDiscoveredVersion.toString()); + + // Saving the state of oAuth2 components. + outState.putInt(OAUTH2_STATUS_ICON, mOAuth2StatusIcon); + outState.putInt(OAUTH2_STATUS_TEXT, mOAuth2StatusText); + CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check); + outState.putBoolean(OAUTH2_IS_CHECKED, oAuth2Check.isChecked()); + if (codeResponseJson != null){ + outState.putString(OAUTH2_CODE_RESULT, codeResponseJson.toString()); + } + // END of saving the state of oAuth2 components. + + // Saving contents of each field. + outState.putString(HOST_URL_TEXT,((TextView) findViewById(R.id.host_URL)).getText().toString().trim()); + outState.putString(ACCOUNT_USERNAME,((TextView) findViewById(R.id.account_username)).getText().toString().trim()); + outState.putString(ACCOUNT_PASSWORD,((TextView) findViewById(R.id.account_password)).getText().toString().trim()); + super.onSaveInstanceState(outState); } @@@ -288,41 -166,6 +288,41 @@@ dialog = working_dialog; break; } + // oAuth2 dialog. We show here to the user the URL and user_code that the user must validate in a web browser. + case OAUTH2_LOGIN_PROGRESS: { + ProgressDialog working_dialog = new ProgressDialog(this); + try { + if (codeResponseJson != null && codeResponseJson.has(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL)) { + working_dialog.setMessage(String.format(getString(R.string.oauth_code_validation_message), + codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL), + codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE))); + } else { + working_dialog.setMessage(String.format("Getting authorization")); + } + } catch (JSONException e) { + Log.e(TAG, "onCreateDialog->JSONException: " + e.toString()); + } + working_dialog.setIndeterminate(true); + working_dialog.setCancelable(true); + working_dialog + .setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + Log.i(TAG, "Login canceled"); + if (mOAuth2GetCodeThread != null) { + mOAuth2GetCodeThread.interrupt(); + finish(); + } + if (tokenReceiver != null) { + unregisterReceiver(tokenReceiver); + tokenReceiver = null; + finish(); + } + } + }); + dialog = working_dialog; + break; + } case DIALOG_SSL_VALIDATOR: { dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this); break; @@@ -351,7 -194,6 +351,7 @@@ switch (id) { case DIALOG_LOGIN_PROGRESS: case DIALOG_CERT_NOT_SAVED: + case OAUTH2_LOGIN_PROGRESS: break; case DIALOG_SSL_VALIDATOR: { ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult); @@@ -361,32 -203,6 +361,32 @@@ Log.e(TAG, "Incorrect dialog called with id = " + id); } } + + @Override + protected void onResume() { + Log.d(TAG, "onResume() start"); + // (old oauth code) Registering token receiver. We must listening to the service that is pooling to the oAuth server for a token. + if (tokenReceiver == null) { + IntentFilter tokenFilter = new IntentFilter(OAuth2GetTokenService.TOKEN_RECEIVED_MESSAGE); + tokenReceiver = new TokenReceiver(); + this.registerReceiver(tokenReceiver,tokenFilter); + } + // (new oauth code) + /*if (mNewRedirectUriCaptured) { + mNewRedirectUriCaptured = false;*/ + if (mNewCapturedUriFromOAuth2Redirection != null) { + getOAuth2AccessTokenFromCapturedRedirection(); + + } + super.onResume(); + } + + @Override + protected void onPause() { + Log.d(TAG, "onPause() start"); + super.onPause(); + } + public void onAuthenticationResult(boolean success, String message) { if (success) { @@@ -431,10 -247,9 +431,10 @@@ AccountAuthenticator.ACCOUNT_TYPE); intent.putExtra(AccountManager.KEY_USERDATA, username); + accManager.setUserData(account, AccountAuthenticator.KEY_OC_URL, + url.toString()); accManager.setUserData(account, - AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable - .getDiscoveredVersion().toString()); + AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString()); accManager.setUserData(account, AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl); @@@ -487,25 -302,9 +487,25 @@@ || url.toLowerCase().startsWith("https://")) { prefix = ""; } - continueConnection(prefix); + CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check); + if (oAuth2Check != null && oAuth2Check.isChecked()) { + startOauthorization(); + + } else { + continueConnection(prefix); + } } + private void startOauthorization() { + // We start a thread to get an authorization code from the oAuth2 server. + setOAuth2ResultIconAndText(R.drawable.progress_small, R.string.oauth_login_connection); + mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_F_AUTHORIZATION_ENDPOINT_URL, this); + //mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(OAuth2Context.OAUTH2_G_DEVICE_GETCODE_URL, this); + mOAuth2GetCodeRunnable.setListener(this, mHandler); + mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable); + mOAuth2GetCodeThread.start(); + } + public void onRegisterClick(View view) { Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register))); setResult(RESULT_CANCELED); @@@ -523,8 -322,8 +523,8 @@@ url = url.substring(0, url.length() - 1); URL uri = null; - String webdav_path = AccountUtils.getWebdavPath(mConnChkRunnable - .getDiscoveredVersion()); + mDiscoveredVersion = mConnChkRunnable.getDiscoveredVersion(); + String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false); if (webdav_path == null) { onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title)); @@@ -628,6 -427,7 +628,6 @@@ findViewById(R.id.buttonOK).setEnabled(mStatusCorrect); } - @Override public void onFocusChange(View view, boolean hasFocus) { if (view.getId() == R.id.host_URL) { if (!hasFocus) { @@@ -637,12 -437,11 +637,12 @@@ setResultIconAndText(R.drawable.progress_small, R.string.auth_testing_connection); //mConnChkRunnable = new ConnectionCheckerRunnable(uri, this); - mConnChkRunnable = new ConnectionCheckOperation(uri, this); + mConnChkRunnable = new ConnectionCheckOperation(uri, this); //mConnChkRunnable.setListener(this, mHandler); //mAuthThread = new Thread(mConnChkRunnable); //mAuthThread.start(); WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this); + mDiscoveredVersion = null; mAuthThread = mConnChkRunnable.execute(client, this, mHandler); } else { findViewById(R.id.refreshButton).setVisibility( @@@ -687,7 -486,9 +687,9 @@@ if (v.getId() == R.id.refreshButton) { onFocusChange(findViewById(R.id.host_URL), false); } else if (v.getId() == R.id.viewPassword) { - TextView view = (TextView) findViewById(R.id.account_password); + EditText view = (EditText) findViewById(R.id.account_password); + int selectionStart = view.getSelectionStart(); + int selectionEnd = view.getSelectionEnd(); int input_type = view.getInputType(); if ((input_type & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) { input_type = InputType.TYPE_CLASS_TEXT @@@ -697,214 -498,13 +699,215 @@@ | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; } view.setInputType(input_type); + view.setSelection(selectionStart, selectionEnd); } } + + @Override protected void onDestroy() { + // We must stop the service thats it's pooling to oAuth2 server for a token. + Intent tokenService = new Intent(this, OAuth2GetTokenService.class); + stopService(tokenService); + + // We stop listening the result of the pooling service. + if (tokenReceiver != null) { + unregisterReceiver(tokenReceiver); + tokenReceiver = null; + finish(); + } + + super.onDestroy(); + } + + // Controlling the oAuth2 checkbox on the activity: hide and show widgets. + public void onOff_check_Click(View view) { + CheckBox oAuth2Check = (CheckBox)view; + changeViewByOAuth2Check(oAuth2Check.isChecked()); + + } + + public void changeViewByOAuth2Check(Boolean checked) { + + EditText oAuth2Url = (EditText) findViewById(R.id.oAuth_URL); + EditText accountUsername = (EditText) findViewById(R.id.account_username); + EditText accountPassword = (EditText) findViewById(R.id.account_password); + ImageView viewPassword = (ImageView) findViewById(R.id.viewPassword); + ImageView auth2ActionIndicator = (ImageView) findViewById(R.id.auth2_action_indicator); + TextView oauth2StatusText = (TextView) findViewById(R.id.oauth2_status_text); + + if (checked) { + oAuth2Url.setVisibility(View.VISIBLE); + accountUsername.setVisibility(View.GONE); + accountPassword.setVisibility(View.GONE); + viewPassword.setVisibility(View.GONE); + auth2ActionIndicator.setVisibility(View.INVISIBLE); + oauth2StatusText.setVisibility(View.INVISIBLE); + } else { + oAuth2Url.setVisibility(View.GONE); + accountUsername.setVisibility(View.VISIBLE); + accountPassword.setVisibility(View.VISIBLE); + viewPassword.setVisibility(View.INVISIBLE); + auth2ActionIndicator.setVisibility(View.GONE); + oauth2StatusText.setVisibility(View.GONE); + } + + } + + // Controlling the oAuth2 result of server connection. + private void setOAuth2ResultIconAndText(int drawable_id, int text_id) { + ImageView iv = (ImageView) findViewById(R.id.auth2_action_indicator); + TextView tv = (TextView) findViewById(R.id.oauth2_status_text); + + if (drawable_id == 0 && text_id == 0) { + iv.setVisibility(View.INVISIBLE); + tv.setVisibility(View.INVISIBLE); + } else { + iv.setImageResource(drawable_id); + tv.setText(text_id); + iv.setVisibility(View.VISIBLE); + tv.setVisibility(View.VISIBLE); + } + } + + // Results from the first call to oAuth2 server : getting the user_code and verification_url. + @Override + public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject responseJson) { + if ((type == ResultOAuthType.OK_SSL)||(type == ResultOAuthType.OK_NO_SSL)) { + codeResponseJson = responseJson; + if (codeResponseJson != null) { + getOAuth2AccessTokenFromJsonResponse(); + } // else - nothing to do here - wait for callback !!! + + } else if (type == ResultOAuthType.HOST_NOT_AVAILABLE) { + setOAuth2ResultIconAndText(R.drawable.common_error, R.string.oauth_connection_url_unavailable); + } + } + + // If the results of getting the user_code and verification_url are OK, we get the received data and we start + // the polling service to oAuth2 server to get a valid token. + private void getOAuth2AccessTokenFromJsonResponse() { + String deviceCode = null; + String verificationUrl = null; + String userCode = null; + int expiresIn = -1; + int interval = -1; + + Log.d(TAG, "ResponseOAuth2->" + codeResponseJson.toString()); + + try { + // We get data that we must show to the user or we will use internally. + verificationUrl = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL); + userCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE); + expiresIn = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_EXPIRES_IN); + + // And we get data that we must use to get a token. + deviceCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_DEVICE_CODE); + interval = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_INTERVAL); + + } catch (JSONException e) { + Log.e(TAG, "Exception accesing data in Json object" + e.toString()); + } + + // Updating status widget to OK. + setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established); + + // Showing the dialog with instructions for the user. + showDialog(OAUTH2_LOGIN_PROGRESS); + + // Loggin all the data. + Log.d(TAG, "verificationUrl->" + verificationUrl); + Log.d(TAG, "userCode->" + userCode); + Log.d(TAG, "deviceCode->" + deviceCode); + Log.d(TAG, "expiresIn->" + expiresIn); + Log.d(TAG, "interval->" + interval); + + // Starting the pooling service. + try { + Intent tokenService = new Intent(this, OAuth2GetTokenService.class); + tokenService.putExtra(OAuth2GetTokenService.TOKEN_URI, OAuth2Context.OAUTH2_G_DEVICE_GETTOKEN_URL); + tokenService.putExtra(OAuth2GetTokenService.TOKEN_DEVICE_CODE, deviceCode); + tokenService.putExtra(OAuth2GetTokenService.TOKEN_INTERVAL, interval); + + startService(tokenService); + } + catch (Exception e) { + Log.e(TAG, "tokenService creation problem :", e); + } + + } + + private void getOAuth2AccessTokenFromCapturedRedirection() { + Map responseValues = new HashMap(); + //String queryParameters = getIntent().getData().getQuery(); + String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery(); + mNewCapturedUriFromOAuth2Redirection = null; + + Log.v(TAG, "Queryparameters (Code) = " + queryParameters); + + String[] pairs = queryParameters.split("&"); + Log.v(TAG, "Pairs (Code) = " + pairs.toString()); + + 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; + responseValues.put(key, value); + sb.append(value + "\n"); + } + + Log.v(TAG, "[" + i + "," + j + "] = " + p); + j++; + } + i++; + } + + + // Updating status widget to OK. + setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established); + + // Showing the dialog with instructions for the user. + showDialog(OAUTH2_LOGIN_PROGRESS); + + // + RemoteOperation operation = new GetOAuth2AccessToken(responseValues); + WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(OAuth2Context.OAUTH2_F_TOKEN_ENDPOINT_URL), getApplicationContext()); + operation.execute(client, this, mHandler); + } + + + + // We get data from the oAuth2 token service with this broadcast receiver. + private class TokenReceiver extends BroadcastReceiver { + /** + * The token is received. + * @author + * {@link BroadcastReceiver} to enable oAuth2 token receiving. + */ + @Override + public void onReceive(Context context, Intent intent) { + @SuppressWarnings("unchecked") + HashMap tokenResponse = (HashMap)intent.getExtras().get(OAuth2GetTokenService.TOKEN_RECEIVED_DATA); + Log.d(TAG, "TokenReceiver->" + tokenResponse.get(OAuth2GetTokenService.TOKEN_ACCESS_TOKEN)); + dismissDialog(OAUTH2_LOGIN_PROGRESS); + + } + } @Override public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - if (operation.equals(mConnChkRunnable)) { + if (operation instanceof ConnectionCheckOperation) { mStatusText = mStatusIcon = 0; mStatusCorrect = false; @@@ -992,126 -592,6 +995,126 @@@ else findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE); findViewById(R.id.buttonOK).setEnabled(mStatusCorrect); + + } else if (operation instanceof GetOAuth2AccessToken) { + + try { + dismissDialog(OAUTH2_LOGIN_PROGRESS); + } catch (IllegalArgumentException e) { + // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens + } + + if (result.isSuccess()) { + + /// time to test the retrieved access token on the ownCloud server + String url = ((TextView) findViewById(R.id.host_URL)).getText() + .toString().trim(); + if (url.endsWith("/")) + url = url.substring(0, url.length() - 1); + + Uri uri = null; + /*String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion); + + if (webdav_path == null) { + onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title)); + return; + }*/ + + String prefix = ""; + if (mIsSslConn) { + prefix = "https://"; + } else { + prefix = "http://"; + } + if (url.toLowerCase().startsWith("http://") + || url.toLowerCase().startsWith("https://")) { + prefix = ""; + } + + try { + mBaseUrl = prefix + url; + //String url_str = prefix + url + webdav_path; + String url_str = prefix + url + "/remote.php/odav"; + uri = Uri.parse(url_str); + + } catch (Exception e) { + // should never happen + onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title)); + return; + } + + showDialog(DIALOG_LOGIN_PROGRESS); + String accessToken = ((GetOAuth2AccessToken)operation).getResultTokenMap().get(OAuth2Context.KEY_ACCESS_TOKEN); + Log.d(TAG, "Got ACCESS TOKEN: " + accessToken); + mAuthChkOperation = new ExistenceCheckOperation("", this, accessToken); + WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(uri, getApplicationContext()); + mAuthChkOperation.execute(client, this, mHandler); + + + } else { + TextView tv = (TextView) findViewById(R.id.oAuth_URL); + tv.setError("A valid authorization could not be obtained"); + + } + + } else if (operation instanceof ExistenceCheckOperation) { + + try { + dismissDialog(DIALOG_LOGIN_PROGRESS); + } catch (IllegalArgumentException e) { + // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens + } + + if (result.isSuccess()) { + TextView tv = (TextView) findViewById(R.id.oAuth_URL); + Log.d(TAG, "Checked access - time to save the account"); + + Uri uri = Uri.parse(mBaseUrl); + String username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong(); + String accountName = username + "@" + uri.getHost(); + if (uri.getPort() >= 0) { + accountName += ":" + uri.getPort(); + } + // TODO - check that accountName does not exist + Account account = new Account(accountName, AccountAuthenticator.ACCOUNT_TYPE); + AccountManager accManager = AccountManager.get(this); + accManager.addAccountExplicitly(account, "", null); // with our implementation, the password is never input in the app + + // Add this account as default in the preferences, if there is none + Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this); + if (defaultAccount == null) { + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit(); + editor.putString("select_oc_account", accountName); + editor.commit(); + } + + /// account data to save by the AccountManager + final Intent intent = new Intent(); + intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE); + intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name); + intent.putExtra(AccountManager.KEY_USERDATA, username); + + accManager.setAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, ((ExistenceCheckOperation) operation).getAccessToken()); + + accManager.setUserData(account, AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable.getDiscoveredVersion().toString()); + accManager.setUserData(account, AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl); + accManager.setUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE"); + + setAccountAuthenticatorResult(intent.getExtras()); + setResult(RESULT_OK, intent); + + /// enforce the first account synchronization + Bundle bundle = new Bundle(); + bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + ContentResolver.requestSync(account, "org.owncloud", bundle); + + finish(); + + } else { + TextView tv = (TextView) findViewById(R.id.oAuth_URL); + tv.setError(result.getLogMessage()); + Log.d(TAG, "Access failed: " + result.getLogMessage()); + } } } @@@ -1124,5 -604,5 +1127,5 @@@ public void onFailedSavingCertificate() { showDialog(DIALOG_CERT_NOT_SAVED); } - + } diff --combined src/com/owncloud/android/ui/fragment/FileDetailFragment.java index 8740f834,2c43278f..f6c25e2c --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@@ -18,9 -18,20 +18,8 @@@ package com.owncloud.android.ui.fragment; import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.httpclient.methods.StringRequestEntity; -import org.apache.commons.httpclient.params.HttpConnectionManagerParams; -import org.apache.http.HttpStatus; -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.protocol.HTTP; -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; -import org.json.JSONObject; + +import org.apache.commons.httpclient.Credentials; - import org.apache.commons.httpclient.UsernamePasswordCredentials; import android.accounts.Account; import android.accounts.AccountManager; @@@ -55,6 -66,7 +54,6 @@@ import android.widget.TextView import android.widget.Toast; import com.actionbarsherlock.app.SherlockFragment; -import com.owncloud.android.AccountUtils; import com.owncloud.android.DisplayUtils; import com.owncloud.android.authenticator.AccountAuthenticator; import com.owncloud.android.datamodel.FileDataStorageManager; @@@ -64,8 -76,7 +63,7 @@@ import com.owncloud.android.files.servi import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; -import com.owncloud.android.network.OwnCloudClientUtils; +import com.owncloud.android.network.BearerCredentials; - import com.owncloud.android.network.OwnCloudClientUtils; import com.owncloud.android.operations.OnRemoteOperationListener; import com.owncloud.android.operations.RemoteOperation; import com.owncloud.android.operations.RemoteOperationResult; @@@ -79,8 -90,10 +77,8 @@@ import com.owncloud.android.ui.activity import com.owncloud.android.ui.activity.TransferServiceGetter; import com.owncloud.android.ui.dialog.EditNameDialog; import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; -import com.owncloud.android.utils.OwnCloudVersion; import com.owncloud.android.R; -import eu.alefzero.webdav.WebdavClient; import eu.alefzero.webdav.WebdavUtils; /** @@@ -142,10 -155,6 +140,6 @@@ public class FileDetailFragment extend 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; - - if(fileToDetail != null && ocAccount != null) { - mLayout = R.layout.file_details_fragment; - } } @@@ -166,6 -175,10 +160,10 @@@ mAccount = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_ACCOUNT); } + if(mFile != null && mAccount != null) { + mLayout = R.layout.file_details_fragment; + } + View view = null; view = inflater.inflate(mLayout, container, false); mView = view; @@@ -295,7 -308,8 +293,7 @@@ } else { mLastRemoteOperation = new SynchronizeFileOperation(mFile, null, mStorageManager, mAccount, true, false, getActivity()); - WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); - mLastRemoteOperation.execute(wc, this, mHandler); + mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity()); // update ui boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; @@@ -409,8 -423,9 +407,7 @@@ mLastRemoteOperation = new RemoveFileOperation( mFile, true, mStorageManager); - WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); - mLastRemoteOperation.execute(wc, this, mHandler); - + mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity()); - boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); } @@@ -446,7 -461,7 +443,7 @@@ * @return True when the fragment was created with the empty layout. */ public boolean isEmpty() { - return mLayout == R.layout.file_details_empty; + return (mLayout == R.layout.file_details_empty || mFile == null || mAccount == null); } @@@ -721,14 -736,14 +718,14 @@@ if (mFile.getRemotePath().equals(uploadRemotePath) || renamedInUpload) { if (uploadWasFine) { - mFile = mStorageManager.getFileByPath(mFile.getRemotePath()); - mFile = mStorageManager.getFileByPath(uploadRemotePath); ++ mFile = mStorageManager.getFileByPath(uploadRemotePath); } if (renamedInUpload) { String newName = (new File(uploadRemotePath)).getName(); Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG); msg.show(); - getSherlockActivity().removeStickyBroadcast(intent); // not the best place to do this; a small refactorization of BroadcastReceivers should be done } + getSherlockActivity().removeStickyBroadcast(intent); // not the best place to do this; a small refactorization of BroadcastReceivers should be done updateFileDetails(false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server } } @@@ -737,7 -752,6 +734,7 @@@ // this is a temporary class for sharing purposes, it need to be replaced in transfer service + /* @SuppressWarnings("unused") private class ShareRunnable implements Runnable { private String mPath; @@@ -840,7 -854,6 +837,7 @@@ } } } + */ public void onDismiss(EditNameDialog dialog) { if (dialog.getResult()) { @@@ -850,7 -863,8 +847,7 @@@ mAccount, newFilename, new FileDataStorageManager(mAccount, getActivity().getContentResolver())); - WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); - mLastRemoteOperation.execute(wc, this, mHandler); + mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity()); boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); } @@@ -936,19 -950,7 +933,19 @@@ */ @Override public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - if (operation.equals(mLastRemoteOperation)) { + if (!result.isSuccess() && result.getCode() == ResultCode.UNAUTHORIZED) { + AccountManager am = AccountManager.get(getSherlockActivity()); + //am.invalidateAuthToken(AccountAuthenticator.ACCOUNT_TYPE, OwnCloudClientUtils.getAuthorizationTokenType(operation.getClient().getCredentials())); + Credentials cred = operation.getClient().getCredentials(); + if (cred instanceof BearerCredentials) { + am.invalidateAuthToken(AccountAuthenticator.ACCOUNT_TYPE, ((BearerCredentials)cred).getAccessToken()); + } else { + am.clearPassword(mAccount); + } + operation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity()); // need a new client instance, so avoid retry() + // TODO si el usuario no se autoriza de nuevo, esto genera un bucle infinito; o un error en la creación del objecto cliente + + } else { if (operation instanceof RemoveFileOperation) { onRemoveFileOperationFinish((RemoveFileOperation)operation, result);