ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
+ Copyright (C) 2012-2013 ownCloud Inc.\r
+ \r
This program is free software: you can redistribute it and/or modify\r
it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation, either version 3 of the License, or\r
+ the Free Software Foundation, either version 2 of the License, or\r
(at your option) any later version.\r
\r
This program is distributed in the hope that it will be useful,\r
along with this program. If not, see <http://www.gnu.org/licenses/>.\r
-->\r
<manifest package="com.owncloud.android"\r
- android:versionCode="103018"\r
- android:versionName="1.3.18" xmlns:android="http://schemas.android.com/apk/res/android">\r
+ android:versionCode="103022"\r
+ android:versionName="1.3.22" xmlns:android="http://schemas.android.com/apk/res/android">\r
\r
<uses-permission android:name="android.permission.GET_ACCOUNTS" />\r
<uses-permission android:name="android.permission.USE_CREDENTIALS" />\r
<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
+ <intent-filter>\r
+ <action android:name="com.owncloud.android.workaround.accounts.CREATE" />\r
+ <category android:name="android.intent.category.DEFAULT" />\r
+ </intent-filter>\r
</activity>\r
\r
<service android:name=".files.services.FileDownloader" >\r
<service 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>
<!--
ownCloud Android client application\r
\r
- Copyright (C) 2012 Bartek Przybylski\r
+ Copyright (C) 2012 Bartek Przybylski
+ Copyright (C) 2012-2013 ownCloud Inc.
+ \r
This program is free software: you can redistribute it and/or modify\r
it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation, either version 3 of the License, or\r
+ the Free Software Foundation, either version 2 of the License, or\r
(at your option) any later version.\r
\r
This program is distributed in the hope that it will be useful,\r
android:layout_weight="1" >
<EditText
- android:id="@+id/host_URL"
+ android:id="@+id/hostUrlInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
android:ems="10"
android:hint="@string/auth_host_url"
android:inputType="textNoSuggestions">
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:src="@drawable/ic_action_refresh_black"
+ android:onClick="onRefreshClick"
android:visibility="invisible" />
</FrameLayout>
android:visibility="invisible" />
</LinearLayout>
+ <CheckBox
+ android:id="@+id/oauth_onOff_check"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:checked="false"
+ android:onClick="onCheckClick"
+ android:text="@string/oauth_check_onoff"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:text="@string/auth_login_details"
android:textAppearance="?android:attr/textAppearanceSmall" />
+ <EditText
+ android:id="@+id/oAuthEntryPoint_1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ems="10"
+ android:enabled="false"
+ android:text="@string/oauth_url_endpoint_auth"
+ android:singleLine="true"
+ android:visibility="gone" >
+
+ <requestFocus />
+ </EditText>
+
+ <EditText
+ android:id="@+id/oAuthEntryPoint_2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ems="10"
+ android:enabled="false"
+ android:text="@string/oauth_url_endpoint_access"
+ android:singleLine="true"
+ android:visibility="gone" >
+
+ <requestFocus />
+ </EditText>
+
<EditText
android:id="@+id/account_username"
android:layout_width="match_parent"
android:id="@+id/account_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
android:ems="10"
android:hint="@string/auth_password"
android:inputType="textPassword"/>
<ImageView
- android:id="@+id/viewPassword"
+ android:id="@+id/viewPasswordButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:src="@android:drawable/ic_menu_view"
+ android:onClick="onViewPasswordClick"
android:visibility="invisible" />
</FrameLayout>
</LinearLayout>
+
+ <!-- LinearLayout
+ android:id="@+id/auth_status_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:visibility="invisible"
+ >
+
+ <ImageView
+ android:id="@+id/auth_status_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="5dp"
+ android:src="@android:drawable/stat_notify_sync"
+ />
+
+ <TextView
+ android:id="@+id/auth_status_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/text_placeholder"
+ />
+
+ </LinearLayout -->
+
+ <TextView
+ android:id="@+id/auth_status_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/text_placeholder"
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="5dp"
+ android:drawableLeft="@android:drawable/stat_notify_sync"
+ android:drawablePadding="5dip"
+ android:visibility="invisible"
+ />
+
+
</LinearLayout>
</FrameLayout>
<!--
ownCloud Android client application\r
\r
- Copyright (C) 2012 Bartek Przybylski\r
+ Copyright (C) 2012 Bartek Przybylski
+ Copyright (C) 2012-2013 ownCloud Inc.
+ \r
This program is free software: you can redistribute it and/or modify\r
it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation, either version 3 of the License, or\r
+ the Free Software Foundation, either version 2 of the License, or\r
(at your option) any later version.\r
\r
This program is distributed in the hope that it will be useful,\r
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:layout_weight="1"
android:orientation="vertical" >
<ImageView
android:layout_weight="1" >
<EditText
- android:id="@+id/host_URL"
+ android:id="@+id/hostUrlInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
android:ems="10"
android:hint="@string/auth_host_url"
android:inputType="textNoSuggestions" >
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_refresh_black"
+ android:onClick="onRefreshClick"
android:layout_gravity="right|center_vertical"
android:visibility="invisible" />
android:layout_height="wrap_content"
android:text="TextView"
android:visibility="invisible" />
-
</LinearLayout>
+ <CheckBox
+ android:id="@+id/oauth_onOff_check"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:checked="false"
+ android:onClick="onCheckClick"
+ android:text="@string/oauth_check_onoff"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_weight="1"
android:text="@string/auth_login_details"
android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <EditText
+ android:id="@+id/oAuthEntryPoint_1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ems="10"
+ android:enabled="false"
+ android:text="@string/oauth_url_endpoint_auth"
+ android:singleLine="true"
+ android:visibility="gone" >
+
+ <requestFocus />
+ </EditText>
+
+ <EditText
+ android:id="@+id/oAuthEntryPoint_2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:ems="10"
+ android:enabled="false"
+ android:text="@string/oauth_url_endpoint_access"
+ android:singleLine="true"
+ android:visibility="gone" >
+
+ <requestFocus />
+ </EditText>
<EditText
android:id="@+id/account_username"
android:id="@+id/account_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
android:ems="10"
android:hint="@string/auth_password"
android:inputType="textPassword"/>
<ImageView
- android:id="@+id/viewPassword"
+ android:id="@+id/viewPasswordButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:src="@android:drawable/ic_menu_view"
+ android:onClick="onViewPasswordClick"
android:visibility="invisible" />
</FrameLayout>
- </LinearLayout>
+ <!-- LinearLayout
+ android:id="@+id/auth_status_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:visibility="invisible"
+ >
+
+ <ImageView
+ android:id="@+id/auth_status_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="5dp"
+ android:src="@android:drawable/stat_notify_sync"
+ />
+
+ <TextView
+ android:id="@+id/auth_status_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/text_placeholder"
+ >
+
+ </LinearLayout -->
+
+ <TextView
+ android:id="@+id/auth_status_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/text_placeholder"
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="5dp"
+ android:drawableLeft="@android:drawable/stat_notify_sync"
+ android:drawablePadding="5dip"
+ android:visibility="invisible"
+ />
+
+ </LinearLayout>
</FrameLayout>
<string name="actionbar_settings">Settings</string>
<string name="prefs_category_general">General</string>
- <string name="prefs_category_trackmydevice">Device tracking</string>
<string name="prefs_add_session">Add new session</string>
<string name="prefs_create_img_thumbnails">Create image thumbnails</string>
<string name="prefs_select_oc_account">Select an account</string>
<string name="sync_string_contacts">Contacts</string>
<string name="sync_fail_ticker">Synchronization failed</string>
<string name="sync_fail_content">Synchronization of %1$s could not be completed</string>
+ <string name="sync_fail_content_unauthorized">Invalid credentials for %1$s</string>
<string name="sync_conflicts_in_favourites_ticker">Conflicts found</string>
<string name="sync_conflicts_in_favourites_content">%1$d kept-in-sync files could not be sync\'ed</string>
<string name="sync_fail_in_favourites_ticker">Kept-in-sync files failed</string>
<string name="auth_wrong_connection_title">Couldn\'t establish connection</string>
<string name="auth_secure_connection">Secure connection established</string>
<string name="auth_login_details">Login details</string>
- <string name="auth_unauthorized">Invalid login / password</string>
+ <string name="auth_unauthorized">Invalid credentials</string>
+ <string name="auth_oauth_error">Unsuccessful authorization</string>
+ <string name="auth_oauth_error_access_denied">Access denied by authorization server</string>
<string name="auth_not_found">Wrong path given</string>
<string name="auth_internal">Internal server error, code %1$d</string>
+ <string name="auth_wtf_reenter_URL">Unexpected state; please, enter the server URL again</string>
+ <string name="auth_expired_oauth_token_toast">Your authorization expired.\nPlease, authorize again</string>
+ <string name="auth_expired_basic_auth_toast">Your saved credentials are invalid.\nPlease, enter the current credentials</string>
<string name="crashlog_message">Application terminated unexpectedly. Would you like to submit a crash report?</string>
<string name="crashlog_send_report">Send report</string>
<string name="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>
<?xml version="1.0" encoding="utf-8"?>
+ <!--
+ ownCloud Android client application
+
+ Copyright (C) 2012 Bartek Przybylski
+ Copyright (C) 2012-2013 ownCloud Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -->
<resources>
<style name="Animations" />
<item name="android:singleLine">true</item>
</style>
+
+ <style name="OAuthDialog" parent="@android:style/Theme.Dialog">
+ <item name="android:windowNoTitle">false</item>
+ </style>
<color name="setup_text_hint">#777777</color>
<color name="setup_text_typed">#000000</color>
/* ownCloud Android client application\r
* Copyright (C) 2012 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";\r
public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";\r
public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav";\r
+ private static final String ODAV_PATH = "/remote.php/odav";\r
public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php";\r
public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php";\r
public static final String STATUS_PATH = "/status.php";\r
break;\r
}\r
}\r
- } else if (ocAccounts.length != 0) {\r
+ }\r
+ \r
+ if (defaultAccount == null && ocAccounts.length != 0) {\r
// we at least need to take first account as fallback\r
defaultAccount = ocAccounts[0];\r
}\r
}\r
\r
\r
- public static void setCurrentOwnCloudAccount(Context context, String name) {\r
- SharedPreferences.Editor appPrefs = PreferenceManager\r
- .getDefaultSharedPreferences(context).edit();\r
- appPrefs.putString("select_oc_account", name);\r
- appPrefs.commit();\r
+ public static boolean setCurrentOwnCloudAccount(Context context, String accountName) {\r
+ boolean result = false;\r
+ if (accountName != null) {\r
+ Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
+ AccountAuthenticator.ACCOUNT_TYPE);\r
+ boolean found = false;\r
+ for (Account account : ocAccounts) {\r
+ found = (account.name.equals(accountName));\r
+ if (found) {\r
+ SharedPreferences.Editor appPrefs = PreferenceManager\r
+ .getDefaultSharedPreferences(context).edit();\r
+ appPrefs.putString("select_oc_account", accountName);\r
+ \r
+ appPrefs.commit();\r
+ result = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ return result;\r
}\r
\r
/**\r
* @param version version of owncloud\r
* @return webdav path for given OC version, null if OC version unknown\r
*/\r
- public static String getWebdavPath(OwnCloudVersion version) {\r
+ public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth) {\r
if (version != null) {\r
+ if (supportsOAuth) {\r
+ return ODAV_PATH;\r
+ }\r
if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
return WEBDAV_PATH_4_0;\r
if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0\r
AccountManager ama = AccountManager.get(context);\r
String baseurl = ama.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL);\r
String strver = ama.getUserData(account, AccountAuthenticator.KEY_OC_VERSION);\r
+ boolean supportsOAuth = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
OwnCloudVersion ver = new OwnCloudVersion(strver);\r
- String webdavpath = getWebdavPath(ver);\r
+ String webdavpath = getWebdavPath(ver, supportsOAuth);\r
\r
if (webdavpath == null) return null;\r
return baseurl + webdavpath;\r
/* ownCloud Android client application\r
* Copyright (C) 2012 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
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
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
// in API7 < this constatant is defined in\r
// Settings.ADD_ACCOUNT_SETTINGS\r
// and Settings.EXTRA_AUTHORITIES\r
- Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");\r
- intent.putExtra("authorities", new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });\r
+ Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);\r
+ intent.putExtra("authorities", new String[] { AccountAuthenticator.AUTHORITY });\r
startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);\r
} else {\r
// since in API7 there is no direct call for\r
\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
/* ownCloud Android client application\r
* Copyright (C) 2012 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
* used by application and all extensions.\r
*/\r
public static final String ACCOUNT_TYPE = "owncloud";\r
+ public static final String AUTHORITY = "org.owncloud";\r
public static final String AUTH_TOKEN_TYPE = "org.owncloud";\r
+ public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";\r
+ public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";\r
+ public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";\r
\r
public static final String KEY_AUTH_TOKEN_TYPE = "authTokenType";\r
public static final String KEY_REQUIRED_FEATURES = "requiredFeatures";\r
* http://server/path or https://owncloud.server\r
*/\r
public static final String KEY_OC_BASE_URL = "oc_base_url";\r
-\r
- private static final String TAG = "AccountAuthenticator";\r
+ /**\r
+ * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.\r
+ */\r
+ public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";\r
+ \r
+ private static final String TAG = AccountAuthenticator.class.getSimpleName();\r
+ \r
private Context mContext;\r
\r
public AccountAuthenticator(Context context) {\r
return e.getFailureBundle();\r
}\r
final Intent intent = new Intent(mContext, AuthenticatorActivity.class);\r
- intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,\r
- response);\r
+ intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);\r
intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);\r
intent.putExtra(KEY_REQUIRED_FEATURES, requiredFeatures);\r
intent.putExtra(KEY_LOGIN_OPTIONS, options);\r
+ intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE);\r
\r
setIntentFlags(intent);\r
+ \r
final Bundle bundle = new Bundle();\r
bundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
return bundle;\r
return null;\r
}\r
\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
@Override\r
public Bundle getAuthToken(AccountAuthenticatorResponse response,\r
Account account, String authTokenType, Bundle options)\r
throws NetworkErrorException {\r
+ /// validate parameters\r
try {\r
validateAccountType(account.type);\r
validateAuthTokenType(authTokenType);\r
e.printStackTrace();\r
return e.getFailureBundle();\r
}\r
+ \r
+ /// check if required token is stored\r
final AccountManager am = AccountManager.get(mContext);\r
- final String password = am.getPassword(account);\r
- if (password != null) {\r
+ String accessToken;\r
+ if (authTokenType.equals(AUTH_TOKEN_TYPE_PASSWORD)) {\r
+ accessToken = am.getPassword(account);\r
+ } else {\r
+ accessToken = am.peekAuthToken(account, authTokenType);\r
+ }\r
+ if (accessToken != null) {\r
final Bundle result = new Bundle();\r
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);\r
result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);\r
- result.putString(AccountManager.KEY_AUTHTOKEN, password);\r
+ result.putString(AccountManager.KEY_AUTHTOKEN, accessToken);\r
return result;\r
}\r
-\r
+ \r
+ /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account\r
final Intent intent = new Intent(mContext, AuthenticatorActivity.class);\r
- intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,\r
- response);\r
+ intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);\r
intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);\r
intent.putExtra(KEY_LOGIN_OPTIONS, options);\r
- intent.putExtra(AuthenticatorActivity.PARAM_USERNAME, account.name);\r
+ intent.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);\r
+ intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);\r
+ \r
\r
final Bundle bundle = new Bundle();\r
bundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
public Bundle hasFeatures(AccountAuthenticatorResponse response,\r
Account account, String[] features) throws NetworkErrorException {\r
final Bundle result = new Bundle();\r
- result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);\r
+ result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);\r
return result;\r
}\r
\r
\r
private void setIntentFlags(Intent intent) {\r
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
- intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);\r
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);\r
+ //intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);\r
+ //intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); // incompatible with the authorization code grant in OAuth\r
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r
intent.addFlags(Intent.FLAG_FROM_BACKGROUND);\r
}\r
\r
private void validateAuthTokenType(String authTokenType)\r
throws UnsupportedAuthTokenTypeException {\r
- if (!authTokenType.equals(AUTH_TOKEN_TYPE)) {\r
+ if (!authTokenType.equals(AUTH_TOKEN_TYPE) &&\r
+ !authTokenType.equals(AUTH_TOKEN_TYPE_PASSWORD) &&\r
+ !authTokenType.equals(AUTH_TOKEN_TYPE_ACCESS_TOKEN) &&\r
+ !authTokenType.equals(AUTH_TOKEN_TYPE_REFRESH_TOKEN) ) {\r
throw new UnsupportedAuthTokenTypeException();\r
}\r
}\r
/* ownCloud Android client application
* Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
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;
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;
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);
/* ownCloud Android client application\r
* Copyright (C) 2012 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
package com.owncloud.android.files.services;\r
\r
import java.io.File;\r
+import java.io.IOException;\r
import java.util.AbstractList;\r
import java.util.Iterator;\r
import java.util.Vector;\r
import 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
\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, 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
/* ownCloud Android client application
* Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
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;
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;
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.accounts.AccountsException;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
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);
/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
import org.apache.http.conn.ssl.X509HostnameVerifier;
import com.owncloud.android.AccountUtils;
+import com.owncloud.android.authenticator.AccountAuthenticator;
import eu.alefzero.webdav.WebdavClient;
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
import android.content.Context;
import android.net.Uri;
+import android.os.Bundle;
import android.util.Log;
public class OwnCloudClientUtils {
- final private static String TAG = "OwnCloudClientFactory";
+ final private static String TAG = OwnCloudClientUtils.class.getSimpleName();
/** Default timeout for waiting data from the server */
public static final int DEFAULT_DATA_TIMEOUT = 60000;
/**
* Creates a WebdavClient setup for an ownCloud account
*
- * @param account The ownCloud account
- * @param context The application context
- * @return A WebdavClient object ready to be used
+ * Do not call this method from the main thread.
+ *
+ * @param account The ownCloud account
+ * @param appContext Android application context
+ * @return A WebdavClient object ready to be used
+ * @throws AuthenticatorException If the authenticator failed to get the authorization token for the account.
+ * @throws OperationCanceledException If the authenticator operation was cancelled while getting the authorization token for the account.
+ * @throws IOException If there was some I/O error while getting the authorization token for the account.
*/
- public static WebdavClient createOwnCloudClient (Account account, Context context) {
- Log.d(TAG, "Creating WebdavClient associated to " + account.name);
+ public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException {
+ //Log.d(TAG, "Creating WebdavClient associated to " + account.name);
- Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(context, account));
- WebdavClient client = createOwnCloudClient(uri, context);
-
- String username = account.name.substring(0, account.name.lastIndexOf('@'));
- String password = AccountManager.get(context).getPassword(account);
- //String password = am.blockingGetAuthToken(mAccount, AccountAuthenticator.AUTH_TOKEN_TYPE, true);
+ Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
+ WebdavClient client = createOwnCloudClient(uri, appContext);
+ AccountManager am = AccountManager.get(appContext);
+ if (am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null) { // TODO avoid a call to getUserData here
+ String accessToken = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, false);
+ client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
- client.setCredentials(username, password);
+ } else {
+ String username = account.name.substring(0, account.name.lastIndexOf('@'));
+ //String password = am.getPassword(account);
+ String password = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_PASSWORD, false);
+ client.setBasicCredentials(username, password);
+ }
return client;
}
- /**
- * Creates a WebdavClient to try a new account before saving it
- *
- * @param uri URL to the ownCloud server
- * @param username User name
- * @param password User password
- * @param context Android context where the WebdavClient is being created.
- * @return A WebdavClient object ready to be used
- */
- public static WebdavClient createOwnCloudClient(Uri uri, String username, String password, Context context) {
- Log.d(TAG, "Creating WebdavClient for " + username + "@" + uri);
-
- WebdavClient client = createOwnCloudClient(uri, context);
-
- client.setCredentials(username, password);
+ public static WebdavClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException {
+ Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
+ WebdavClient client = createOwnCloudClient(uri, appContext);
+ AccountManager am = AccountManager.get(appContext);
+ if (am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null) { // TODO avoid a call to getUserData here
+ AccountManagerFuture<Bundle> future = am.getAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, null, currentActivity, null, null);
+ Bundle result = future.getResult();
+ String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
+ //String accessToken = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, false);
+ if (accessToken == null) throw new AuthenticatorException("WTF!");
+ client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
+
+ } else {
+ String username = account.name.substring(0, account.name.lastIndexOf('@'));
+ //String password = am.getPassword(account);
+ //String password = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_PASSWORD, false);
+ AccountManagerFuture<Bundle> future = am.getAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_PASSWORD, null, currentActivity, null, null);
+ Bundle result = future.getResult();
+ String password = result.getString(AccountManager.KEY_AUTHTOKEN);
+ client.setBasicCredentials(username, password);
+ }
return client;
}
-
+
/**
* Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections.
* @return A WebdavClient object ready to be used
*/
public static WebdavClient createOwnCloudClient(Uri uri, Context context) {
- Log.d(TAG, "Creating WebdavClient for " + uri);
+ //Log.d(TAG, "Creating WebdavClient for " + uri);
//allowSelfsignedCertificates(true);
try {
/**
- * Allows or disallows self-signed certificates in ownCloud servers to reach
- *
- * @param allow 'True' to allow, 'false' to disallow
- */
- public static void allowSelfsignedCertificates(boolean allow) {
- Protocol pr = null;
- try {
- pr = Protocol.getProtocol("https");
- if (pr != null && mDefaultHttpsProtocol == null) {
- mDefaultHttpsProtocol = pr;
- }
- } catch (IllegalStateException e) {
- // nothing to do here; really
- }
- boolean isAllowed = (pr != null && pr.getSocketFactory() instanceof EasySSLSocketFactory);
- if (allow && !isAllowed) {
- Protocol.registerProtocol("https", new Protocol("https", new EasySSLSocketFactory(), 443));
- } else if (!allow && isAllowed) {
- if (mDefaultHttpsProtocol != null) {
- Protocol.registerProtocol("https", mDefaultHttpsProtocol);
- }
- }
- }
-
-
- /**
* Registers or unregisters the proper components for advanced SSL handling.
* @throws IOException
*/
return mConnManager;
}
+
}
--- /dev/null
- * Copyright (C) 2012 Bartek Przybylski
+/* ownCloud Android client application
- * the Free Software Foundation, either version 3 of the License, or
++ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.owncloud.android.AccountUtils;
+import com.owncloud.android.utils.OwnCloudVersion;
+
+import eu.alefzero.webdav.WebdavClient;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.util.Log;
+
+public class OwnCloudServerCheckOperation extends RemoteOperation {
+
+ /** Maximum time to wait for a response from the server when the connection is being tested, in MILLISECONDs. */
+ public static final int TRY_CONNECTION_TIMEOUT = 5000;
+
+ private static final String TAG = OwnCloudServerCheckOperation.class.getSimpleName();
+
+ private String mUrl;
+ private RemoteOperationResult mLatestResult;
+ private Context mContext;
+ private OwnCloudVersion mOCVersion;
+
+ public OwnCloudServerCheckOperation(String url, Context context) {
+ mUrl = url;
+ mContext = context;
+ mOCVersion = null;
+ }
+
+ public OwnCloudVersion getDiscoveredVersion() {
+ return mOCVersion;
+ }
+
+ private boolean tryConnection(WebdavClient wc, String urlSt) {
+ boolean retval = false;
+ GetMethod get = null;
+ try {
+ get = new GetMethod(urlSt);
+ int status = wc.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT);
+ String response = get.getResponseBodyAsString();
+ if (status == HttpStatus.SC_OK) {
+ JSONObject json = new JSONObject(response);
+ if (!json.getBoolean("installed")) {
+ mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+ } else {
+ mOCVersion = new OwnCloudVersion(json.getString("version"));
+ if (!mOCVersion.isVersionValid()) {
+ mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
+
+ } else {
+ mLatestResult = new RemoteOperationResult(urlSt.startsWith("https://") ?
+ RemoteOperationResult.ResultCode.OK_SSL :
+ RemoteOperationResult.ResultCode.OK_NO_SSL
+ );
+
+ retval = true;
+ }
+ }
+
+ } else {
+ mLatestResult = new RemoteOperationResult(false, status);
+ }
+
+ } catch (JSONException e) {
+ mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+
+ } catch (Exception e) {
+ mLatestResult = new RemoteOperationResult(e);
+
+ } finally {
+ if (get != null)
+ get.releaseConnection();
+ }
+
+ if (mLatestResult.isSuccess()) {
+ Log.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+
+ } else if (mLatestResult.getException() != null) {
+ Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
+
+ } else {
+ Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+ }
+
+ return retval;
+ }
+
+ private boolean isOnline() {
+ ConnectivityManager cm = (ConnectivityManager) mContext
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ return cm != null && cm.getActiveNetworkInfo() != null
+ && cm.getActiveNetworkInfo().isConnectedOrConnecting();
+ }
+
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ if (!isOnline()) {
+ return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
+ }
+ if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) {
+ tryConnection(client, mUrl + AccountUtils.STATUS_PATH);
+
+ } else {
+ client.setBaseUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH));
+ boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH);
+ if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) {
+ Log.d(TAG, "establishing secure connection failed, trying non secure connection");
+ client.setBaseUri(Uri.parse("http://" + mUrl + AccountUtils.STATUS_PATH));
+ tryConnection(client, "http://" + mUrl + AccountUtils.STATUS_PATH);
+ }
+ }
+ return mLatestResult;
+ }
+
+}
/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
*/
package com.owncloud.android.operations;
+import java.io.IOException;
+
+import org.apache.commons.httpclient.Credentials;
+
+import com.owncloud.android.authenticator.AccountAuthenticator;
+import com.owncloud.android.network.BearerCredentials;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountsException;
+import android.app.Activity;
+import android.content.Context;
import android.os.Handler;
+import android.util.Log;
import eu.alefzero.webdav.WebdavClient;
*/
public abstract class RemoteOperation implements Runnable {
- /** Object to interact with the ownCloud server */
+ private static final String TAG = RemoteOperation.class.getSimpleName();
+
+ /** ownCloud account in the remote ownCloud server to operate */
+ private Account mAccount = null;
+
+ /** Android Application context */
+ private Context mContext = null;
+
+ /** Object to interact with the remote server */
private WebdavClient mClient = null;
/** Callback object to notify about the execution of the remote operation */
/** Handler to the thread where mListener methods will be called */
private Handler mListenerHandler = null;
+ /** Activity */
+ private Activity mCallerActivity;
+
/**
* Abstract method to implement the operation in derived classes.
*/
protected abstract RemoteOperationResult run(WebdavClient client);
+
+ /**
+ * Synchronously executes the remote operation on the received ownCloud account.
+ *
+ * Do not call this method from the main thread.
+ *
+ * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
+ *
+ * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
+ * @param context Android context for the component calling the method.
+ * @return Result of the operation.
+ */
+ public final RemoteOperationResult execute(Account account, Context context) {
+ if (account == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
+ if (context == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
+ mAccount = account;
+ mContext = context.getApplicationContext();
+ try {
+ mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
+ } catch (Exception e) {
+ Log.e(TAG, "Error while trying to access to " + mAccount.name, e);
+ return new RemoteOperationResult(e);
+ }
+ return run(mClient);
+ }
+
/**
* Synchronously executes the remote operation
*
+ * Do not call this method from the main thread.
+ *
* @param client Client object to reach an ownCloud server during the execution of the operation.
* @return Result of the operation.
*/
}
+ /**
+ * Asynchronously executes the remote operation
+ *
+ * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
+ *
+ * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
+ * @param context Android context for the component calling the method.
+ * @param listener Listener to be notified about the execution of the operation.
+ * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
+ * @return Thread were the remote operation is executed.
+ */
+ public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
+ if (account == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
+ if (context == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
+ mAccount = account;
+ mContext = context.getApplicationContext();
+ mCallerActivity = callerActivity;
+ mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below
+
+ if (listener == null) {
+ throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
+ }
+ mListener = listener;
+
+ if (listenerHandler == null) {
+ throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
+ }
+ mListenerHandler = listenerHandler;
+
+ Thread runnerThread = new Thread(this);
+ runnerThread.start();
+ return runnerThread;
+ }
+
+
/**
* Asynchronously executes the remote operation
*
* Asynchronous execution of the operation
* started by {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)},
* and result posting.
+ *
+ * TODO refactor && clean the code; now it's a mess
*/
@Override
public final void run() {
- final RemoteOperationResult result = execute(mClient);
+ RemoteOperationResult result = null;
+ boolean repeat = false;
+ do {
+ try{
+ if (mClient == null) {
+ if (mAccount != null && mContext != null) {
+ if (mCallerActivity != null) {
+ mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext, mCallerActivity);
+ } else {
+ mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
+ }
+ } else {
+ throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account");
+ }
+ }
+
+ } catch (IOException e) {
+ Log.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e));
+ result = new RemoteOperationResult(e);
+
+ } catch (AccountsException e) {
+ Log.e(TAG, "Error while trying to access to " + mAccount.name, e);
+ result = new RemoteOperationResult(e);
+ }
+ if (result == null)
+ result = run(mClient);
+
+ repeat = false;
+ if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() && result.getCode() == ResultCode.UNAUTHORIZED) {
+ /// fail due to lack of authorization in an operation performed in foreground
+ AccountManager am = AccountManager.get(mContext);
+ Credentials cred = mClient.getCredentials();
+ if (cred instanceof BearerCredentials) {
+ am.invalidateAuthToken(AccountAuthenticator.ACCOUNT_TYPE, ((BearerCredentials)cred).getAccessToken());
+ } else {
+ am.clearPassword(mAccount);
+ }
+ mClient = null;
+ repeat = true; // when repeated, the creation of a new OwnCloudClient after erasing the saved credentials will trigger the login activity
+ result = null;
+ }
+ } while (repeat);
+
+ final RemoteOperationResult resultToSend = result;
if (mListenerHandler != null && mListener != null) {
mListenerHandler.post(new Runnable() {
@Override
public void run() {
- mListener.onRemoteOperationFinish(RemoteOperation.this, result);
+ mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
}
});
}
}
-
-
+
+
+ /**
+ * Returns the current client instance to access the remote server.
+ *
+ * @return Current client instance to access the remote server.
+ */
+ public final WebdavClient getClient() {
+ return mClient;
+ }
+
}
/* ownCloud Android client application
* Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
/** Generated - should be refreshed every time the class changes!! */
private static final long serialVersionUID = -7805531062432602444L;
-
public enum ResultCode {
OK,
INVALID_LOCAL_FILE_NAME,
INVALID_OVERWRITE,
CONFLICT,
+ OAUTH2_ERROR,
SYNC_CONFLICT,
LOCAL_STORAGE_FULL,
LOCAL_STORAGE_NOT_MOVED,
- LOCAL_STORAGE_NOT_COPIED
+ LOCAL_STORAGE_NOT_COPIED,
+ OAUTH2_ERROR_ACCESS_DENIED
}
private boolean mSuccess = false;
/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
*/
public class RenameFileOperation extends RemoteOperation {
- private static final String TAG = RemoveFileOperation.class.getSimpleName();
+ private static final String TAG = RenameFileOperation.class.getSimpleName();
private static final int RENAME_READ_TIMEOUT = 10000;
private static final int RENAME_CONNECTION_TIMEOUT = 5000;
/* ownCloud Android client application\r
* Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
return null;\r
}\r
\r
- protected void initClientForCurrentAccount() throws UnknownHostException {\r
+ protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException {\r
if (AccountUtils.constructFullURLForAccount(getContext(), account) == null) {\r
throw new UnknownHostException();\r
}\r
/* ownCloud Android client application\r
* Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
package com.owncloud.android.syncadapter;\r
\r
import java.io.IOException;\r
-import java.net.UnknownHostException;\r
import java.util.ArrayList;\r
import java.util.HashMap;\r
import java.util.List;\r
import com.owncloud.android.operations.SynchronizeFolderOperation;\r
import com.owncloud.android.operations.UpdateOCVersionOperation;\r
import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
+import com.owncloud.android.ui.activity.AuthenticatorActivity;\r
import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;\r
+\r
import android.accounts.Account;\r
+import android.accounts.AccountsException;\r
import android.app.Notification;\r
import android.app.NotificationManager;\r
import android.app.PendingIntent;\r
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
RemoteOperationResult.ResultCode code = failedResult.getCode();\r
return (code.equals(RemoteOperationResult.ResultCode.SSL_ERROR) ||\r
code.equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) ||\r
+ code.equals(RemoteOperationResult.ResultCode.UNAUTHORIZED) ||\r
code.equals(RemoteOperationResult.ResultCode.BAD_OC_VERSION) ||\r
code.equals(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED));\r
}\r
private void notifyFailedSynchronization() {\r
Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());\r
notification.flags |= Notification.FLAG_AUTO_CANCEL;\r
- // TODO put something smart in the contentIntent below\r
+ boolean needsToUpdateCredentials = (mLastFailedResult != null && mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED);\r
+ // TODO put something smart in the contentIntent below for all the possible errors\r
notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
- notification.setLatestEventInfo(getContext().getApplicationContext(), \r
- getContext().getString(R.string.sync_fail_ticker), \r
- String.format(getContext().getString(R.string.sync_fail_content), getAccount().name), \r
- notification.contentIntent);\r
+ if (needsToUpdateCredentials) {\r
+ // let the user update credentials with one click\r
+ Intent updateAccountCredentials = new Intent(getContext(), AuthenticatorActivity.class);\r
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());\r
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);\r
+ updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
+ updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r
+ updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);\r
+ notification.contentIntent = PendingIntent.getActivity(getContext(), (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);\r
+ notification.setLatestEventInfo(getContext().getApplicationContext(), \r
+ getContext().getString(R.string.sync_fail_ticker), \r
+ String.format(getContext().getString(R.string.sync_fail_content_unauthorized), getAccount().name), \r
+ notification.contentIntent);\r
+ Log.e(TAG, "NEEDS TO UPDATE CREDENTIALS");\r
+ } else {\r
+ notification.setLatestEventInfo(getContext().getApplicationContext(), \r
+ getContext().getString(R.string.sync_fail_ticker), \r
+ String.format(getContext().getString(R.string.sync_fail_content), getAccount().name), \r
+ notification.contentIntent);\r
+ }\r
((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification);\r
}\r
\r
/* ownCloud Android client application
* Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
/// the account set as default changed since this activity was created
// trigger synchronization
- ContentResolver.cancelSync(null, AccountAuthenticator.AUTH_TOKEN_TYPE);
+ ContentResolver.cancelSync(null, AccountAuthenticator.AUTHORITY);
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
- ContentResolver.requestSync(AccountUtils.getCurrentOwnCloudAccount(this), AccountAuthenticator.AUTH_TOKEN_TYPE, bundle);
+ ContentResolver.requestSync(AccountUtils.getCurrentOwnCloudAccount(this), AccountAuthenticator.AUTHORITY, bundle);
// restart the main activity
Intent i = new Intent(this, FileDisplayActivity.class);
Intent intent = new Intent(
android.provider.Settings.ACTION_ADD_ACCOUNT);
intent.putExtra("authorities",
- new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
+ new String[] { AccountAuthenticator.AUTHORITY });
startActivity(intent);
return true;
}
/* ownCloud Android client application\r
* Copyright (C) 2012 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
\r
package com.owncloud.android.ui.activity;\r
\r
-import java.net.MalformedURLException;\r
-import java.net.URL;\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.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.OwnCloudServerCheckOperation;\r
+import com.owncloud.android.operations.ExistenceCheckOperation;\r
+import com.owncloud.android.operations.OAuth2GetAccessToken;\r
import com.owncloud.android.operations.OnRemoteOperationListener;\r
import com.owncloud.android.operations.RemoteOperation;\r
import com.owncloud.android.operations.RemoteOperationResult;\r
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
\r
import android.accounts.Account;\r
import android.accounts.AccountAuthenticatorActivity;\r
import android.text.InputType;\r
import android.util.Log;\r
import android.view.View;\r
-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 android.widget.Toast;\r
+\r
import com.owncloud.android.R;\r
\r
import eu.alefzero.webdav.WebdavClient;\r
* This Activity is used to add an ownCloud account to the App\r
* \r
* @author Bartek Przybylski\r
- * \r
+ * @author David A. Velasco\r
*/\r
public class AuthenticatorActivity extends AccountAuthenticatorActivity\r
- implements OnAuthenticationResultListener, OnConnectCheckListener, OnRemoteOperationListener, OnSslValidatorListener, \r
- OnFocusChangeListener, OnClickListener {\r
+ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeListener {\r
+\r
+ private static final String TAG = AuthenticatorActivity.class.getSimpleName();\r
+\r
+ public static final String EXTRA_ACCOUNT = "ACCOUNT";\r
+ public static final String EXTRA_USER_NAME = "USER_NAME";\r
+ public static final String EXTRA_HOST_NAME = "HOST_NAME";\r
+ public static final String EXTRA_ACTION = "ACTION";\r
+ \r
+ private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT";\r
+ private static final String KEY_OC_VERSION = "OC_VERSION";\r
+ private static final String KEY_ACCOUNT = "ACCOUNT";\r
+ private static final String KEY_STATUS_TEXT = "STATUS_TEXT";\r
+ private static final String KEY_STATUS_ICON = "STATUS_ICON";\r
+ private static final String KEY_STATUS_CORRECT = "STATUS_CORRECT";\r
+ private static final String KEY_IS_SSL_CONN = "IS_SSL_CONN";\r
+ private static final String KEY_OAUTH2_STATUS_TEXT = "OAUTH2_STATUS_TEXT";\r
+ private static final String KEY_OAUTH2_STATUS_ICON = "OAUTH2_STATUS_ICON";\r
\r
private static final int DIALOG_LOGIN_PROGRESS = 0;\r
private static final int DIALOG_SSL_VALIDATOR = 1;\r
private static final int DIALOG_CERT_NOT_SAVED = 2;\r
+ private static final int DIALOG_OAUTH2_LOGIN_PROGRESS = 3;\r
\r
- private static final String TAG = "AuthActivity";\r
+ public static final byte ACTION_CREATE = 0;\r
+ public static final byte ACTION_UPDATE_TOKEN = 1;\r
\r
- private Thread mAuthThread;\r
- private AuthenticationRunnable mAuthRunnable;\r
- //private ConnectionCheckerRunnable mConnChkRunnable = null;\r
- private ConnectionCheckOperation mConnChkRunnable;\r
- private final Handler mHandler = new Handler();\r
- private String mBaseUrl;\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 String mHostBaseUrl;\r
+ private OwnCloudVersion mDiscoveredVersion;\r
+ \r
private int mStatusText, mStatusIcon;\r
private boolean mStatusCorrect, mIsSslConn;\r
+ private int mOAuth2StatusText, mOAuth2StatusIcon; \r
+ \r
+ private final Handler mHandler = new Handler();\r
+ private Thread mOperationThread;\r
+ private OwnCloudServerCheckOperation mOcServerChkOperation;\r
+ private ExistenceCheckOperation mAuthCheckOperation;\r
private RemoteOperationResult mLastSslUntrustedServerResult;\r
\r
- public static final String PARAM_USERNAME = "param_Username";\r
- public static final String PARAM_HOSTNAME = "param_Hostname";\r
-\r
+ //private Thread mOAuth2GetCodeThread;\r
+ //private OAuth2GetAuthorizationToken mOAuth2GetCodeRunnable; \r
+ //private TokenReceiver tokenReceiver;\r
+ //private JSONObject mCodeResponseJson; \r
+ private Uri mNewCapturedUriFromOAuth2Redirection;\r
+ \r
+ private AccountManager mAccountMgr;\r
+ private boolean mJustCreated;\r
+ private byte mAction;\r
+ private Account mAccount;\r
+ \r
+ private ImageView mRefreshButton;\r
+ private ImageView mViewPasswordButton;\r
+ private EditText mHostUrlInput;\r
+ private EditText mUsernameInput;\r
+ private EditText mPasswordInput;\r
+ private CheckBox mOAuth2Check;\r
+ private String mOAuthAccessToken;\r
+ private View mOkButton;\r
+ private TextView mAuthStatusLayout;\r
+ \r
+ private TextView mOAuthAuthEndpointText;\r
+ private TextView mOAuthTokenEndpointText;\r
+ \r
+ \r
+ /**\r
+ * {@inheritDoc}\r
+ * \r
+ * IMPORTANT ENTRY POINT 1: activity is shown to the user\r
+ */\r
@Override\r
protected void onCreate(Bundle savedInstanceState) {\r
super.onCreate(savedInstanceState);\r
getWindow().requestFeature(Window.FEATURE_NO_TITLE);\r
+ \r
+ /// set view and get references to view elements\r
setContentView(R.layout.account_setup);\r
- ImageView iv = (ImageView) findViewById(R.id.refreshButton);\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
-\r
- if (savedInstanceState != null) {\r
- mStatusIcon = savedInstanceState.getInt(STATUS_ICON);\r
- mStatusText = savedInstanceState.getInt(STATUS_TEXT);\r
- mStatusCorrect = savedInstanceState.getBoolean(STATUS_CORRECT);\r
- mIsSslConn = savedInstanceState.getBoolean(IS_SSL_CONN);\r
- setResultIconAndText(mStatusIcon, mStatusText);\r
- findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);\r
- if (!mStatusCorrect)\r
- iv.setVisibility(View.VISIBLE);\r
- else\r
- iv.setVisibility(View.INVISIBLE);\r
+ mRefreshButton = (ImageView) findViewById(R.id.refreshButton);\r
+ mViewPasswordButton = (ImageView) findViewById(R.id.viewPasswordButton);\r
+ mHostUrlInput = (EditText) findViewById(R.id.hostUrlInput);\r
+ mUsernameInput = (EditText) findViewById(R.id.account_username);\r
+ mPasswordInput = (EditText) findViewById(R.id.account_password);\r
+ mOAuthAuthEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_1);\r
+ mOAuthTokenEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_2);\r
+ mOAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
+ mOkButton = findViewById(R.id.buttonOK);\r
+ mAuthStatusLayout = (TextView) findViewById(R.id.auth_status_text); \r
+ \r
\r
- } else {\r
+ /// complete label for 'register account' button\r
+ Button b = (Button) findViewById(R.id.account_register);\r
+ if (b != null) {\r
+ b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name)));\r
+ }\r
+\r
+ /// bind view elements to listeners\r
+ mHostUrlInput.setOnFocusChangeListener(this);\r
+ mPasswordInput.setOnFocusChangeListener(this);\r
+ \r
+ /// initialization\r
+ mAccountMgr = AccountManager.get(this);\r
+ mNewCapturedUriFromOAuth2Redirection = null; // TODO save?\r
+ mAction = getIntent().getByteExtra(EXTRA_ACTION, ACTION_CREATE); \r
+ mAccount = null;\r
+\r
+ if (savedInstanceState == null) {\r
+ /// connection state and info\r
mStatusText = mStatusIcon = 0;\r
mStatusCorrect = false;\r
mIsSslConn = false;\r
- }\r
- iv.setOnClickListener(this);\r
- iv2.setOnClickListener(this);\r
- tv.setOnFocusChangeListener(this);\r
- tv2.setOnFocusChangeListener(this);\r
+ \r
+ /// retrieve extras from intent\r
+ String tokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
+ boolean oAuthRequired = AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(tokenType);\r
+ \r
+ mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
+ if (mAccount != null) {\r
+ String ocVersion = mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION);\r
+ if (ocVersion != null) {\r
+ mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
+ }\r
+ mHostBaseUrl = mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL);\r
+ mHostUrlInput.setText(mHostBaseUrl);\r
+ String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
+ mUsernameInput.setText(userName);\r
+ oAuthRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
+ }\r
+ mOAuth2Check.setChecked(oAuthRequired);\r
+ changeViewByOAuth2Check(oAuthRequired);\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
+ } else {\r
+ loadSavedInstanceState(savedInstanceState);\r
+ }\r
+ \r
+ if (mAction == ACTION_UPDATE_TOKEN) {\r
+ /// lock things that should not change\r
+ mHostUrlInput.setEnabled(false);\r
+ mUsernameInput.setEnabled(false);\r
+ mOAuth2Check.setVisibility(View.GONE);\r
+ checkOcServer(); \r
}\r
+ \r
+ mPasswordInput.setText(""); // clean password to avoid social hacking (disadvantage: password in removed if the device is turned aside)\r
+ mJustCreated = true;\r
}\r
\r
+\r
+ /**\r
+ * Saves relevant state before {@link #onPause()}\r
+ * \r
+ * Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag, intended to defer the \r
+ * processing of the redirection caught in {@link #onNewIntent(Intent)} until {@link #onResume()} \r
+ * \r
+ * See {@link #loadSavedInstanceState(Bundle)}\r
+ */\r
@Override\r
protected void onSaveInstanceState(Bundle outState) {\r
- outState.putInt(STATUS_ICON, mStatusIcon);\r
- outState.putInt(STATUS_TEXT, mStatusText);\r
- outState.putBoolean(STATUS_CORRECT, mStatusCorrect);\r
super.onSaveInstanceState(outState);\r
+ \r
+ /// connection state and info\r
+ outState.putInt(KEY_STATUS_TEXT, mStatusText);\r
+ outState.putInt(KEY_STATUS_ICON, mStatusIcon);\r
+ outState.putBoolean(KEY_STATUS_CORRECT, mStatusCorrect);\r
+ outState.putBoolean(KEY_IS_SSL_CONN, mIsSslConn);\r
+\r
+ /// server data\r
+ if (mDiscoveredVersion != null) \r
+ outState.putString(KEY_OC_VERSION, mDiscoveredVersion.toString());\r
+ outState.putString(KEY_HOST_URL_TEXT, mHostBaseUrl);\r
+ \r
+ /// account data, if updating\r
+ if (mAccount != null)\r
+ outState.putParcelable(KEY_ACCOUNT, mAccount);\r
+ \r
+ // Saving the state of oAuth2 components.\r
+ outState.putInt(KEY_OAUTH2_STATUS_ICON, mOAuth2StatusIcon);\r
+ outState.putInt(KEY_OAUTH2_STATUS_TEXT, mOAuth2StatusText);\r
+ \r
+ /* Leave old OAuth flow\r
+ if (codeResponseJson != null){\r
+ outState.putString(KEY_OAUTH2_CODE_RESULT, codeResponseJson.toString());\r
+ }\r
+ */\r
}\r
\r
+\r
+ /**\r
+ * Loads saved state\r
+ * \r
+ * See {@link #onSaveInstanceState(Bundle)}.\r
+ * \r
+ * @param savedInstanceState Saved state, as received in {@link #onCreate(Bundle)}.\r
+ */\r
+ private void loadSavedInstanceState(Bundle savedInstanceState) {\r
+ /// connection state and info\r
+ mStatusCorrect = savedInstanceState.getBoolean(KEY_STATUS_CORRECT);\r
+ mIsSslConn = savedInstanceState.getBoolean(KEY_IS_SSL_CONN);\r
+ mStatusText = savedInstanceState.getInt(KEY_STATUS_TEXT);\r
+ mStatusIcon = savedInstanceState.getInt(KEY_STATUS_ICON);\r
+ updateConnStatus();\r
+ \r
+ /// UI settings depending upon connection\r
+ mOkButton.setEnabled(mStatusCorrect); // TODO really necessary?\r
+ if (!mStatusCorrect)\r
+ mRefreshButton.setVisibility(View.VISIBLE); // seems that setting visibility is necessary\r
+ else\r
+ mRefreshButton.setVisibility(View.INVISIBLE);\r
+ \r
+ /// server data\r
+ String ocVersion = savedInstanceState.getString(KEY_OC_VERSION);\r
+ if (ocVersion != null)\r
+ mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
+ mHostBaseUrl = savedInstanceState.getString(KEY_HOST_URL_TEXT);\r
+ \r
+ // account data, if updating\r
+ mAccount = savedInstanceState.getParcelable(KEY_ACCOUNT);\r
+ \r
+ // state of oAuth2 components\r
+ mOAuth2StatusIcon = savedInstanceState.getInt(KEY_OAUTH2_STATUS_ICON);\r
+ mOAuth2StatusText = savedInstanceState.getInt(KEY_OAUTH2_STATUS_TEXT);\r
+ \r
+ /* Leave old OAuth flow\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(KEY_OAUTH2_CODE_RESULT)) {\r
+ codeResponseJson = new JSONObject(savedInstanceState.getString(KEY_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
+ }\r
+\r
+ \r
+ /**\r
+ * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION request\r
+ * is caught here.\r
+ * \r
+ * To make this possible, this activity needs to be qualified with android:launchMode = "singleTask" in the\r
+ * AndroidManifest.xml file.\r
+ */\r
@Override\r
- protected Dialog onCreateDialog(int id) {\r
- Dialog dialog = null;\r
- switch (id) {\r
- case DIALOG_LOGIN_PROGRESS: {\r
- ProgressDialog working_dialog = new ProgressDialog(this);\r
- working_dialog.setMessage(getResources().getString(\r
- R.string.auth_trying_to_login));\r
- working_dialog.setIndeterminate(true);\r
- working_dialog.setCancelable(true);\r
- working_dialog\r
- .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
- @Override\r
- public void onCancel(DialogInterface dialog) {\r
- Log.i(TAG, "Login canceled");\r
- if (mAuthThread != null) {\r
- mAuthThread.interrupt();\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
- }\r
- case DIALOG_CERT_NOT_SAVED: {\r
- AlertDialog.Builder builder = new AlertDialog.Builder(this);\r
- builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));\r
- builder.setCancelable(false);\r
- builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {\r
- @Override\r
- public void onClick(DialogInterface dialog, int which) {\r
- dialog.dismiss();\r
- };\r
- });\r
- dialog = builder.create();\r
- break;\r
+ protected void onNewIntent (Intent intent) {\r
+ Log.d(TAG, "onNewIntent()");\r
+ Uri data = intent.getData();\r
+ if (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI)) {\r
+ mNewCapturedUriFromOAuth2Redirection = data;\r
}\r
- default:\r
- Log.e(TAG, "Incorrect dialog called with id = " + id);\r
- }\r
- return dialog;\r
}\r
\r
+ \r
+ /**\r
+ * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION, and \r
+ * deferred in {@link #onNewIntent(Intent)}, is processed here.\r
+ */\r
@Override\r
- protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {\r
- switch (id) {\r
- case DIALOG_LOGIN_PROGRESS:\r
- case DIALOG_CERT_NOT_SAVED:\r
- break;\r
- case DIALOG_SSL_VALIDATOR: {\r
- ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);\r
- break;\r
+ protected void onResume() {\r
+ super.onResume();\r
+ // the state of mOAuth2Check is automatically recovered between configuration changes, but not before onCreate() finishes; so keep the next lines here\r
+ changeViewByOAuth2Check(mOAuth2Check.isChecked()); \r
+ if (mAction == ACTION_UPDATE_TOKEN && mJustCreated) {\r
+ if (mOAuth2Check.isChecked())\r
+ Toast.makeText(this, R.string.auth_expired_oauth_token_toast, Toast.LENGTH_LONG).show();\r
+ else\r
+ Toast.makeText(this, R.string.auth_expired_basic_auth_toast, Toast.LENGTH_LONG).show();\r
}\r
- default:\r
- Log.e(TAG, "Incorrect dialog called with id = " + id);\r
+ \r
+ \r
+ /* LEAVE OLD OAUTH FLOW ; \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 (mNewCapturedUriFromOAuth2Redirection != null) {\r
+ getOAuth2AccessTokenFromCapturedRedirection(); \r
}\r
+ \r
+ mJustCreated = false;\r
}\r
+ \r
+ \r
+ @Override protected void onDestroy() { \r
+ super.onDestroy();\r
\r
- public void onAuthenticationResult(boolean success, String message) {\r
- if (success) {\r
- TextView username_text = (TextView) findViewById(R.id.account_username), password_text = (TextView) findViewById(R.id.account_password);\r
+ /* LEAVE OLD OAUTH FLOW\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
+ }*/\r
\r
- URL url;\r
- try {\r
- url = new URL(message);\r
- } catch (MalformedURLException e) {\r
- // should never happen\r
- Log.e(getClass().getName(), "Malformed URL: " + message);\r
- return;\r
- }\r
+ } \r
+ \r
+ \r
+ /**\r
+ * Parses the redirection with the response to the GET AUTHORIZATION request to the \r
+ * oAuth server and requests for the access token (GET ACCESS TOKEN)\r
+ */\r
+ private void getOAuth2AccessTokenFromCapturedRedirection() {\r
+ /// Parse data from OAuth redirection\r
+ String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();\r
+ mNewCapturedUriFromOAuth2Redirection = null;\r
+ \r
+ /// Showing the dialog with instructions for the user.\r
+ showDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
\r
- String username = username_text.getText().toString().trim();\r
- String accountName = username + "@" + url.getHost();\r
- if (url.getPort() >= 0) {\r
- accountName += ":" + url.getPort();\r
- }\r
- Account account = new Account(accountName,\r
- AccountAuthenticator.ACCOUNT_TYPE);\r
- AccountManager accManager = AccountManager.get(this);\r
- accManager.addAccountExplicitly(account, password_text.getText()\r
- .toString(), null);\r
-\r
- // Add this account as default in the preferences, if there is none\r
- // already\r
- Account defaultAccount = AccountUtils\r
- .getCurrentOwnCloudAccount(this);\r
- if (defaultAccount == null) {\r
- SharedPreferences.Editor editor = PreferenceManager\r
- .getDefaultSharedPreferences(this).edit();\r
- editor.putString("select_oc_account", accountName);\r
- editor.commit();\r
- }\r
+ /// GET ACCESS TOKEN to the oAuth server \r
+ RemoteOperation operation = new OAuth2GetAccessToken(queryParameters);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth_url_endpoint_access)), getApplicationContext());\r
+ operation.execute(client, this, mHandler);\r
+ }\r
+ \r
\r
- final Intent intent = new Intent();\r
- intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,\r
- AccountAuthenticator.ACCOUNT_TYPE);\r
- intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);\r
- intent.putExtra(AccountManager.KEY_AUTHTOKEN,\r
- AccountAuthenticator.ACCOUNT_TYPE);\r
- intent.putExtra(AccountManager.KEY_USERDATA, username);\r
-\r
- accManager.setUserData(account,\r
- AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable\r
- .getDiscoveredVersion().toString());\r
+ \r
+ /**\r
+ * Handles the change of focus on the text inputs for the server URL and the password\r
+ */\r
+ public void onFocusChange(View view, boolean hasFocus) {\r
+ if (view.getId() == R.id.hostUrlInput) {\r
+ onUrlInputFocusChanged((TextView) view, hasFocus);\r
\r
- accManager.setUserData(account,\r
- AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);\r
-\r
- setAccountAuthenticatorResult(intent.getExtras());\r
- setResult(RESULT_OK, intent);\r
- Bundle bundle = new Bundle();\r
- bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
- //getContentResolver().startSync(ProviderTableMeta.CONTENT_URI,\r
- // bundle);\r
- ContentResolver.requestSync(account, "org.owncloud", bundle);\r
-\r
- /*\r
- * if\r
- * (mConnChkRunnable.getDiscoveredVersion().compareTo(OwnCloudVersion\r
- * .owncloud_v2) >= 0) { Intent i = new Intent(this,\r
- * ExtensionsAvailableActivity.class); startActivity(i); }\r
- */\r
+ } else if (view.getId() == R.id.account_password) {\r
+ onPasswordFocusChanged((TextView) view, hasFocus);\r
+ }\r
+ }\r
+ \r
\r
- finish();\r
+ /**\r
+ * Handles changes in focus on the text input for the server URL.\r
+ * \r
+ * IMPORTANT ENTRY POINT 2: When (!hasFocus), user wrote the server URL and changed to \r
+ * other field. The operation to check the existence of the server in the entered URL is\r
+ * started. \r
+ * \r
+ * When hasFocus: user 'comes back' to write again the server URL.\r
+ * \r
+ * @param hostInput TextView with the URL input field receiving the change of focus.\r
+ * @param hasFocus 'True' if focus is received, 'false' if is lost\r
+ */\r
+ private void onUrlInputFocusChanged(TextView hostInput, boolean hasFocus) {\r
+ if (!hasFocus) {\r
+ checkOcServer();\r
+ \r
} else {\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
- TextView tv = (TextView) findViewById(R.id.account_username);\r
- tv.setError(message + " "); // the extra spaces are a workaround for an ugly bug: \r
- // 1. insert wrong credentials and connect\r
- // 2. put the focus on the user name field with using hardware controls (don't touch the screen); the error is shown UNDER the field\r
- // 3. touch the user name field; the software keyboard appears; the error popup is moved OVER the field and SHRINKED in width, losing the last word\r
- // Seen, at least, in Android 2.x devices\r
+ // avoids that the 'connect' button can be clicked if the test was previously passed\r
+ mOkButton.setEnabled(false); \r
+ }\r
+ }\r
+\r
+\r
+ private void checkOcServer() {\r
+ String uri = mHostUrlInput.getText().toString().trim();\r
+ if (uri.length() != 0) {\r
+ mStatusText = R.string.auth_testing_connection;\r
+ mStatusIcon = R.drawable.progress_small;\r
+ updateConnStatus();\r
+ /** TODO cancel previous connection check if the user tries to ammend a wrong URL \r
+ if(mConnChkOperation != null) {\r
+ mConnChkOperation.cancel();\r
+ } */\r
+ mOcServerChkOperation = new OwnCloudServerCheckOperation(uri, this);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this);\r
+ mHostBaseUrl = "";\r
+ mDiscoveredVersion = null;\r
+ mOperationThread = mOcServerChkOperation.execute(client, this, mHandler);\r
+ } else {\r
+ mRefreshButton.setVisibility(View.INVISIBLE);\r
+ mStatusText = 0;\r
+ mStatusIcon = 0;\r
+ updateConnStatus();\r
}\r
}\r
+\r
+\r
+ /**\r
+ * Handles changes in focus on the text input for the password (basic authorization).\r
+ * \r
+ * When (hasFocus), the button to toggle password visibility is shown.\r
+ * \r
+ * When (!hasFocus), the button is made invisible and the password is hidden.\r
+ * \r
+ * @param passwordInput TextView with the password input field receiving the change of focus.\r
+ * @param hasFocus 'True' if focus is received, 'false' if is lost\r
+ */\r
+ private void onPasswordFocusChanged(TextView passwordInput, boolean hasFocus) {\r
+ if (hasFocus) {\r
+ mViewPasswordButton.setVisibility(View.VISIBLE);\r
+ } else {\r
+ int input_type = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD;\r
+ passwordInput.setInputType(input_type);\r
+ mViewPasswordButton.setVisibility(View.INVISIBLE);\r
+ }\r
+ }\r
+\r
+\r
+ \r
+ /**\r
+ * Cancels the authenticator activity\r
+ * \r
+ * IMPORTANT ENTRY POINT 3: Never underestimate the importance of cancellation\r
+ * \r
+ * This method is bound in the layout/acceoun_setup.xml resource file.\r
+ * \r
+ * @param view Cancel button\r
+ */\r
public void onCancelClick(View view) {\r
- setResult(RESULT_CANCELED);\r
+ setResult(RESULT_CANCELED); // TODO review how is this related to AccountAuthenticator\r
finish();\r
}\r
\r
+ \r
+ \r
+ /**\r
+ * Checks the credentials of the user in the root of the ownCloud server\r
+ * before creating a new local account.\r
+ * \r
+ * For basic authorization, a check of existence of the root folder is\r
+ * performed.\r
+ * \r
+ * For OAuth, starts the flow to get an access token; the credentials test \r
+ * is postponed until it is available.\r
+ * \r
+ * IMPORTANT ENTRY POINT 4\r
+ * \r
+ * @param view OK button\r
+ */\r
public void onOkClick(View view) {\r
- String prefix = "";\r
- String url = ((TextView) findViewById(R.id.host_URL)).getText()\r
- .toString().trim();\r
- if (mIsSslConn) {\r
- prefix = "https://";\r
- } else {\r
- prefix = "http://";\r
+ // this check should be unnecessary\r
+ if (mDiscoveredVersion == null || !mDiscoveredVersion.isVersionValid() || mHostBaseUrl == null || mHostBaseUrl.length() == 0) {\r
+ mStatusIcon = R.drawable.common_error;\r
+ mStatusText = R.string.auth_wtf_reenter_URL;\r
+ updateConnStatus();\r
+ mOkButton.setEnabled(false);\r
+ Log.wtf(TAG, "The user was allowed to click 'connect' to an unchecked server!!");\r
+ return;\r
}\r
- if (url.toLowerCase().startsWith("http://")\r
- || url.toLowerCase().startsWith("https://")) {\r
- prefix = "";\r
+ \r
+ if (mOAuth2Check.isChecked()) {\r
+ startOauthorization();\r
+ \r
+ } else {\r
+ checkBasicAuthorization();\r
}\r
- continueConnection(prefix);\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
- startActivity(register);\r
+ \r
+ /**\r
+ * Tests the credentials entered by the user performing a check of existence on \r
+ * the root folder of the ownCloud server.\r
+ */\r
+ private void checkBasicAuthorization() {\r
+ /// get the path to the root folder through WebDAV from the version server\r
+ String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false);\r
+ \r
+ /// get basic credentials entered by user\r
+ String username = mUsernameInput.getText().toString();\r
+ String password = mPasswordInput.getText().toString();\r
+ \r
+ /// be gentle with the user\r
+ showDialog(DIALOG_LOGIN_PROGRESS);\r
+ \r
+ /// test credentials accessing the root folder\r
+ mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this);\r
+ client.setBasicCredentials(username, password);\r
+ mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
}\r
\r
- private void continueConnection(String prefix) {\r
- String url = ((TextView) findViewById(R.id.host_URL)).getText()\r
- .toString().trim();\r
- String username = ((TextView) findViewById(R.id.account_username))\r
- .getText().toString();\r
- String password = ((TextView) findViewById(R.id.account_password))\r
- .getText().toString();\r
- if (url.endsWith("/"))\r
- url = url.substring(0, url.length() - 1);\r
-\r
- URL uri = null;\r
- String webdav_path = AccountUtils.getWebdavPath(mConnChkRunnable\r
- .getDiscoveredVersion());\r
- \r
- if (webdav_path == null) {\r
- onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));\r
- return;\r
- }\r
+\r
+ /**\r
+ * Starts the OAuth 'grant type' flow to get an access token, with \r
+ * a GET AUTHORIZATION request to the BUILT-IN authorization server. \r
+ */\r
+ private void startOauthorization() {\r
+ // be gentle with the user\r
+ mStatusIcon = R.drawable.progress_small;\r
+ mStatusText = R.string.oauth_login_connection;\r
+ updateAuthStatus();\r
\r
- try {\r
- mBaseUrl = prefix + url;\r
- String url_str = prefix + url + webdav_path;\r
- uri = new URL(url_str);\r
- } catch (MalformedURLException e) {\r
- // should never happen\r
- onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title));\r
- return;\r
+ // GET AUTHORIZATION request\r
+ /*\r
+ mOAuth2GetCodeRunnable = new OAuth2GetAuthorizationToken(, this);\r
+ mOAuth2GetCodeRunnable.setListener(this, mHandler);\r
+ mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable);\r
+ mOAuth2GetCodeThread.start();\r
+ */\r
+ \r
+ //if (mGrantType.equals(OAuth2Context.OAUTH2_AUTH_CODE_GRANT_TYPE)) {\r
+ Uri uri = Uri.parse(getString(R.string.oauth_url_endpoint_auth));\r
+ Uri.Builder uriBuilder = uri.buildUpon();\r
+ uriBuilder.appendQueryParameter(OAuth2Context.CODE_RESPONSE_TYPE, OAuth2Context.OAUTH2_CODE_RESPONSE_TYPE);\r
+ uriBuilder.appendQueryParameter(OAuth2Context.CODE_REDIRECT_URI, OAuth2Context.MY_REDIRECT_URI); \r
+ uriBuilder.appendQueryParameter(OAuth2Context.CODE_CLIENT_ID, OAuth2Context.OAUTH2_F_CLIENT_ID);\r
+ uriBuilder.appendQueryParameter(OAuth2Context.CODE_SCOPE, OAuth2Context.OAUTH2_F_SCOPE);\r
+ //uriBuilder.appendQueryParameter(OAuth2Context.CODE_STATE, whateverwewant);\r
+ uri = uriBuilder.build();\r
+ Log.d(TAG, "Starting browser to view " + uri.toString());\r
+ Intent i = new Intent(Intent.ACTION_VIEW, uri);\r
+ startActivity(i);\r
+ //}\r
+ }\r
+\r
+ \r
+ /**\r
+ * Callback method invoked when a RemoteOperation executed by this Activity finishes.\r
+ * \r
+ * Dispatches the operation flow to the right method.\r
+ */\r
+ @Override\r
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
+\r
+ if (operation instanceof OwnCloudServerCheckOperation) {\r
+ onOcServerCheckFinish((OwnCloudServerCheckOperation) operation, result);\r
+ \r
+ } else if (operation instanceof OAuth2GetAccessToken) {\r
+ onGetOAuthAccessTokenFinish((OAuth2GetAccessToken)operation, result);\r
+ \r
+ } else if (operation instanceof ExistenceCheckOperation) {\r
+ onAuthorizationCheckFinish((ExistenceCheckOperation)operation, result);\r
+ \r
}\r
+ }\r
+ \r
\r
- showDialog(DIALOG_LOGIN_PROGRESS);\r
- mAuthRunnable = new AuthenticationRunnable(uri, username, password, this);\r
- mAuthRunnable.setOnAuthenticationResultListener(this, mHandler);\r
- mAuthThread = new Thread(mAuthRunnable);\r
- mAuthThread.start();\r
+ /**\r
+ * Processes the result of the server check performed when the user finishes the enter of the\r
+ * server URL.\r
+ * \r
+ * @param operation Server check performed.\r
+ * @param result Result of the check.\r
+ */\r
+ private void onOcServerCheckFinish(OwnCloudServerCheckOperation operation, RemoteOperationResult result) {\r
+ /// update status icon and text\r
+ updateStatusIconAndText(result);\r
+ updateConnStatus();\r
+\r
+ /// save result state\r
+ mStatusCorrect = result.isSuccess();\r
+ mIsSslConn = (result.getCode() == ResultCode.OK_SSL);\r
+ \r
+ /// very special case (TODO: move to a common place for all the remote operations)\r
+ if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {\r
+ mLastSslUntrustedServerResult = result;\r
+ showDialog(DIALOG_SSL_VALIDATOR); \r
+ }\r
+ \r
+ /// update the visibility of the 'retry connection' button\r
+ if (!mStatusCorrect)\r
+ mRefreshButton.setVisibility(View.VISIBLE);\r
+ else\r
+ mRefreshButton.setVisibility(View.INVISIBLE);\r
+ \r
+ /// retrieve discovered version and normalize server URL\r
+ mDiscoveredVersion = operation.getDiscoveredVersion();\r
+ mHostBaseUrl = mHostUrlInput.getText().toString().trim();\r
+ if (!mHostBaseUrl.toLowerCase().startsWith("http://") &&\r
+ !mHostBaseUrl.toLowerCase().startsWith("https://")) {\r
+ \r
+ if (mIsSslConn) {\r
+ mHostBaseUrl = "https://" + mHostBaseUrl;\r
+ } else {\r
+ mHostBaseUrl = "http://" + mHostBaseUrl;\r
+ }\r
+ \r
+ }\r
+ if (mHostBaseUrl.endsWith("/"))\r
+ mHostBaseUrl = mHostBaseUrl.substring(0, mHostBaseUrl.length() - 1);\r
+ \r
+ /// allow or not the user try to access the server\r
+ mOkButton.setEnabled(mStatusCorrect);\r
}\r
\r
- @Override\r
- public void onConnectionCheckResult(ResultType type) {\r
+\r
+ /**\r
+ * Chooses the right icon and text to show to the user for the received operation result.\r
+ * \r
+ * @param result Result of a remote operation performed in this activity\r
+ */\r
+ private void updateStatusIconAndText(RemoteOperationResult result) {\r
mStatusText = mStatusIcon = 0;\r
- mStatusCorrect = false;\r
- String t_url = ((TextView) findViewById(R.id.host_URL)).getText()\r
- .toString().trim().toLowerCase();\r
\r
- switch (type) {\r
+ switch (result.getCode()) {\r
case OK_SSL:\r
- mIsSslConn = true;\r
mStatusIcon = android.R.drawable.ic_secure;\r
mStatusText = R.string.auth_secure_connection;\r
- mStatusCorrect = true;\r
break;\r
+ \r
case OK_NO_SSL:\r
- mIsSslConn = false;\r
- mStatusCorrect = true;\r
- if (t_url.startsWith("http://") ) {\r
+ case OK:\r
+ if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {\r
mStatusText = R.string.auth_connection_established;\r
mStatusIcon = R.drawable.ic_ok;\r
} else {\r
mStatusIcon = android.R.drawable.ic_partial_secure;\r
}\r
break;\r
+ \r
+ case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
+ mStatusIcon = R.drawable.common_error;\r
+ mStatusText = R.string.auth_ssl_unverified_server_title;\r
+ break;\r
+ \r
case BAD_OC_VERSION:\r
mStatusIcon = R.drawable.common_error;\r
mStatusText = R.string.auth_bad_oc_version_title;\r
mStatusIcon = R.drawable.common_error;\r
mStatusText = R.string.auth_incorrect_address_title;\r
break;\r
- case SSL_UNVERIFIED_SERVER:\r
+ \r
+ case SSL_ERROR:\r
mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_ssl_unverified_server_title;\r
+ mStatusText = R.string.auth_ssl_general_error_title;\r
break;\r
- case SSL_INIT_ERROR:\r
+ \r
+ case UNAUTHORIZED:\r
mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_ssl_general_error_title;\r
+ mStatusText = R.string.auth_unauthorized;\r
break;\r
case HOST_NOT_AVAILABLE:\r
mStatusIcon = R.drawable.common_error;\r
mStatusIcon = R.drawable.common_error;\r
mStatusText = R.string.auth_not_configured_title;\r
break;\r
+ case FILE_NOT_FOUND:\r
+ mStatusIcon = R.drawable.common_error;\r
+ mStatusText = R.string.auth_incorrect_path_title;\r
+ break;\r
+ case OAUTH2_ERROR:\r
+ mStatusIcon = R.drawable.common_error;\r
+ mStatusText = R.string.auth_oauth_error;\r
+ break;\r
+ case OAUTH2_ERROR_ACCESS_DENIED:\r
+ mStatusIcon = R.drawable.common_error;\r
+ mStatusText = R.string.auth_oauth_error_access_denied;\r
+ break;\r
+ case UNHANDLED_HTTP_CODE:\r
case UNKNOWN_ERROR:\r
mStatusIcon = R.drawable.common_error;\r
mStatusText = R.string.auth_unknown_error_title;\r
break;\r
- case FILE_NOT_FOUND:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_incorrect_path_title;\r
+ \r
+ default:\r
break;\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Processes the result of the request for and access token send \r
+ * to an OAuth authorization server.\r
+ * \r
+ * @param operation Operation performed requesting the access token.\r
+ * @param result Result of the operation.\r
+ */\r
+ private void onGetOAuthAccessTokenFinish(OAuth2GetAccessToken operation, RemoteOperationResult result) {\r
+ try {\r
+ dismissDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
+ } catch (IllegalArgumentException e) {\r
+ // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+ }\r
+\r
+ String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, true);\r
+ if (result.isSuccess() && webdav_path != null) {\r
+ /// be gentle with the user\r
+ showDialog(DIALOG_LOGIN_PROGRESS);\r
+ \r
+ /// time to test the retrieved access token on the ownCloud server\r
+ mOAuthAccessToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Context.KEY_ACCESS_TOKEN);\r
+ Log.d(TAG, "Got ACCESS TOKEN: " + mOAuthAccessToken);\r
+ mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this);\r
+ client.setBearerCredentials(mOAuthAccessToken);\r
+ mAuthCheckOperation.execute(client, this, mHandler);\r
+ \r
+ } else {\r
+ updateStatusIconAndText(result);\r
+ updateAuthStatus();\r
+ Log.d(TAG, "Access failed: " + result.getLogMessage());\r
+ }\r
+ }\r
+\r
+ \r
+ /**\r
+ * Processes the result of the access check performed to try the user credentials.\r
+ * \r
+ * Creates a new account through the AccountManager.\r
+ * \r
+ * @param operation Access check performed.\r
+ * @param result Result of the operation.\r
+ */\r
+ private void onAuthorizationCheckFinish(ExistenceCheckOperation operation, RemoteOperationResult result) {\r
+ try {\r
+ dismissDialog(DIALOG_LOGIN_PROGRESS);\r
+ } catch (IllegalArgumentException e) {\r
+ // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+ }\r
+ \r
+ if (result.isSuccess()) {\r
+ Log.d(TAG, "Successful access - time to save the account");\r
+\r
+ if (mAction == ACTION_CREATE) {\r
+ createAccount();\r
+ \r
+ } else {\r
+ updateToken();\r
+ }\r
+ \r
+ finish();\r
+ \r
+ } else {\r
+ updateStatusIconAndText(result);\r
+ updateAuthStatus();\r
+ Log.d(TAG, "Access failed: " + result.getLogMessage());\r
+ }\r
+ }\r
+\r
+ \r
+ /**\r
+ * Sets the proper response to get that the Account Authenticator that started this activity saves \r
+ * a new authorization token for mAccount.\r
+ */\r
+ private void updateToken() {\r
+ Bundle response = new Bundle();\r
+ response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
+ response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);\r
+ boolean isOAuth = mOAuth2Check.isChecked();\r
+ if (isOAuth) {\r
+ response.putString(AccountManager.KEY_AUTHTOKEN, mOAuthAccessToken);\r
+ // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
+ mAccountMgr.setAuthToken(mAccount, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, mOAuthAccessToken);\r
+ } else {\r
+ response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());\r
+ mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());\r
+ }\r
+ setAccountAuthenticatorResult(response);\r
+ }\r
+\r
+\r
+ /**\r
+ * Creates a new account through the Account Authenticator that started this activity. \r
+ * \r
+ * This makes the account permanent.\r
+ * \r
+ * TODO Decide how to name the OAuth accounts\r
+ * TODO Minimize the direct interactions with the account manager; seems that not all the operations \r
+ * in the current code are really necessary, provided that right extras are returned to the Account\r
+ * Authenticator through setAccountAuthenticatorResult \r
+ */\r
+ private void createAccount() {\r
+ /// create and save new ownCloud account\r
+ boolean isOAuth = mOAuth2Check.isChecked();\r
+ \r
+ Uri uri = Uri.parse(mHostBaseUrl);\r
+ String username = mUsernameInput.getText().toString().trim();\r
+ if (isOAuth) {\r
+ username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong(); // TODO change this to something readable\r
+ } \r
+ String accountName = username + "@" + uri.getHost();\r
+ if (uri.getPort() >= 0) {\r
+ accountName += ":" + uri.getPort();\r
+ }\r
+ mAccount = new Account(accountName, AccountAuthenticator.ACCOUNT_TYPE);\r
+ if (isOAuth) {\r
+ mAccountMgr.addAccountExplicitly(mAccount, "", null); // with our implementation, the password is never input in the app\r
+ } else {\r
+ mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null);\r
+ }\r
+\r
+ /// add the new account as default in preferences, if there is none already\r
+ Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);\r
+ if (defaultAccount == null) {\r
+ SharedPreferences.Editor editor = PreferenceManager\r
+ .getDefaultSharedPreferences(this).edit();\r
+ editor.putString("select_oc_account", accountName);\r
+ editor.commit();\r
+ }\r
+\r
+ /// prepare result to return to the Authenticator\r
+ // TODO check again what the Authenticator makes with it; probably has the same effect as addAccountExplicitly, but it's not well done\r
+ final Intent intent = new Intent(); \r
+ intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE);\r
+ intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
+ if (!isOAuth)\r
+ intent.putExtra(AccountManager.KEY_AUTHTOKEN, AccountAuthenticator.ACCOUNT_TYPE); // TODO check this; not sure it's right; maybe\r
+ intent.putExtra(AccountManager.KEY_USERDATA, username);\r
+ if (isOAuth) {\r
+ mAccountMgr.setAuthToken(mAccount, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, mOAuthAccessToken);\r
+ }\r
+ /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA\r
+ mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString());\r
+ mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL, mHostBaseUrl);\r
+ if (isOAuth)\r
+ mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE"); // TODO this flag should be unnecessary\r
+ \r
+ setAccountAuthenticatorResult(intent.getExtras());\r
+ setResult(RESULT_OK, intent);\r
+ \r
+ /// immediately request for the synchronization of the new account\r
+ Bundle bundle = new Bundle();\r
+ bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
+ ContentResolver.requestSync(mAccount, AccountAuthenticator.AUTHORITY, bundle);\r
+ }\r
+\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ * \r
+ * Necessary to update the contents of the SSL Dialog\r
+ * \r
+ * TODO move to some common place for all possible untrusted SSL failures\r
+ */\r
+ @Override\r
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {\r
+ switch (id) {\r
+ case DIALOG_LOGIN_PROGRESS:\r
+ case DIALOG_CERT_NOT_SAVED:\r
+ case DIALOG_OAUTH2_LOGIN_PROGRESS:\r
+ break;\r
+ case DIALOG_SSL_VALIDATOR: {\r
+ ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);\r
+ break;\r
+ }\r
default:\r
- Log.e(TAG, "Incorrect connection checker result type: " + type);\r
+ Log.e(TAG, "Incorrect dialog called with id = " + id);\r
}\r
- setResultIconAndText(mStatusIcon, mStatusText);\r
- if (!mStatusCorrect)\r
- findViewById(R.id.refreshButton).setVisibility(View.VISIBLE);\r
- else\r
- findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);\r
- findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);\r
}\r
\r
+ \r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
@Override\r
- public void onFocusChange(View view, boolean hasFocus) {\r
- if (view.getId() == R.id.host_URL) {\r
- if (!hasFocus) {\r
- TextView tv = ((TextView) findViewById(R.id.host_URL));\r
- String uri = tv.getText().toString().trim();\r
- if (uri.length() != 0) {\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.setListener(this, mHandler);\r
- //mAuthThread = new Thread(mConnChkRunnable);\r
- //mAuthThread.start();\r
- WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this);\r
- mAuthThread = mConnChkRunnable.execute(client, this, mHandler);\r
- } else {\r
- findViewById(R.id.refreshButton).setVisibility(\r
- View.INVISIBLE);\r
- setResultIconAndText(0, 0);\r
+ protected Dialog onCreateDialog(int id) {\r
+ Dialog dialog = null;\r
+ switch (id) {\r
+ case DIALOG_LOGIN_PROGRESS: {\r
+ /// simple progress dialog\r
+ ProgressDialog working_dialog = new ProgressDialog(this);\r
+ working_dialog.setMessage(getResources().getString(R.string.auth_trying_to_login));\r
+ working_dialog.setIndeterminate(true);\r
+ working_dialog.setCancelable(true);\r
+ working_dialog\r
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
+ @Override\r
+ public void onCancel(DialogInterface dialog) {\r
+ /// TODO study if this is enough\r
+ Log.i(TAG, "Login canceled");\r
+ if (mOperationThread != null) {\r
+ mOperationThread.interrupt();\r
+ finish();\r
+ }\r
+ }\r
+ });\r
+ dialog = working_dialog;\r
+ break;\r
+ }\r
+ case DIALOG_OAUTH2_LOGIN_PROGRESS: {\r
+ /// oAuth2 dialog. We show here to the user the URL and user_code that the user must validate in a web browser. - OLD!\r
+ // TODO optimize this dialog\r
+ ProgressDialog working_dialog = new ProgressDialog(this);\r
+ /* Leave the old OAuth flow\r
+ try {\r
+ if (mCodeResponseJson != null && mCodeResponseJson.has(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL)) {\r
+ working_dialog.setMessage(String.format(getString(R.string.oauth_code_validation_message), \r
+ mCodeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL), \r
+ mCodeResponseJson.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
+ finish();\r
}\r
- } else {\r
- // avoids that the 'connect' button can be clicked if the test was previously passed\r
- findViewById(R.id.buttonOK).setEnabled(false); \r
- }\r
- } else if (view.getId() == R.id.account_password) {\r
- ImageView iv = (ImageView) findViewById(R.id.viewPassword);\r
- if (hasFocus) {\r
- iv.setVisibility(View.VISIBLE);\r
- } else {\r
- TextView v = (TextView) findViewById(R.id.account_password);\r
- int input_type = InputType.TYPE_CLASS_TEXT\r
- | InputType.TYPE_TEXT_VARIATION_PASSWORD;\r
- v.setInputType(input_type);\r
- iv.setVisibility(View.INVISIBLE);\r
- }\r
+ });\r
+ dialog = working_dialog;\r
+ break;\r
}\r
+ case DIALOG_SSL_VALIDATOR: {\r
+ /// TODO start to use new dialog interface, at least for this (it is a FragmentDialog already)\r
+ dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);\r
+ break;\r
+ }\r
+ case DIALOG_CERT_NOT_SAVED: {\r
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);\r
+ builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));\r
+ builder.setCancelable(false);\r
+ builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {\r
+ @Override\r
+ public void onClick(DialogInterface dialog, int which) {\r
+ dialog.dismiss();\r
+ };\r
+ });\r
+ dialog = builder.create();\r
+ break;\r
+ }\r
+ default:\r
+ Log.e(TAG, "Incorrect dialog called with id = " + id);\r
+ }\r
+ return dialog;\r
+ }\r
+\r
+ \r
+ /**\r
+ * Starts and activity to open the 'new account' page in the ownCloud web site\r
+ * \r
+ * @param view 'Account register' button\r
+ */\r
+ public void onRegisterClick(View view) {\r
+ Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register)));\r
+ setResult(RESULT_CANCELED);\r
+ startActivity(register);\r
}\r
\r
- private void setResultIconAndText(int drawable_id, int text_id) {\r
+ \r
+ /**\r
+ * Updates the content and visibility state of the icon and text associated\r
+ * to the last check on the ownCloud server.\r
+ */\r
+ private void updateConnStatus() {\r
ImageView iv = (ImageView) findViewById(R.id.action_indicator);\r
TextView tv = (TextView) findViewById(R.id.status_text);\r
\r
- if (drawable_id == 0 && text_id == 0) {\r
+ if (mStatusIcon == 0 && mStatusText == 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.setImageResource(mStatusIcon);\r
+ tv.setText(mStatusText);\r
iv.setVisibility(View.VISIBLE);\r
tv.setVisibility(View.VISIBLE);\r
}\r
}\r
+ \r
+ \r
+ /**\r
+ * Updates the content and visibility state of the icon and text associated\r
+ * to the interactions with the OAuth authorization server.\r
+ */\r
+ private void updateAuthStatus() {\r
+ /*ImageView iv = (ImageView) findViewById(R.id.auth_status_icon);\r
+ TextView tv = (TextView) findViewById(R.id.auth_status_text);*/\r
+\r
+ if (mStatusIcon == 0 && mStatusText == 0) {\r
+ mAuthStatusLayout.setVisibility(View.INVISIBLE);\r
+ /*iv.setVisibility(View.INVISIBLE);\r
+ tv.setVisibility(View.INVISIBLE);*/\r
+ } else {\r
+ mAuthStatusLayout.setText(mStatusText);\r
+ mAuthStatusLayout.setCompoundDrawablesWithIntrinsicBounds(mStatusIcon, 0, 0, 0);\r
+ /*iv.setImageResource(mStatusIcon);\r
+ tv.setText(mStatusText);\r
+ /*iv.setVisibility(View.VISIBLE);\r
+ tv.setVisibility(View.VISIBLE);^*/\r
+ mAuthStatusLayout.setVisibility(View.VISIBLE);\r
+ }\r
+ } \r
\r
+ \r
+ /**\r
+ * Called when the refresh button in the input field for ownCloud host is clicked.\r
+ * \r
+ * Performs a new check on the URL in the input field.\r
+ * \r
+ * @param view Refresh 'button'\r
+ */\r
+ public void onRefreshClick(View view) {\r
+ onFocusChange(mRefreshButton, false);\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Called when the eye icon in the password field is clicked.\r
+ * \r
+ * Toggles the visibility of the password in the field. \r
+ * \r
+ * @param view 'View password' 'button'\r
+ */\r
+ public void onViewPasswordClick(View view) {\r
+ int selectionStart = mPasswordInput.getSelectionStart();\r
+ int selectionEnd = mPasswordInput.getSelectionEnd();\r
+ int input_type = mPasswordInput.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_PASSWORD;\r
+ } else {\r
+ input_type = InputType.TYPE_CLASS_TEXT\r
+ | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;\r
+ }\r
+ mPasswordInput.setInputType(input_type);\r
+ mPasswordInput.setSelection(selectionStart, selectionEnd);\r
+ } \r
+ \r
+ \r
+ /**\r
+ * Called when the checkbox for OAuth authorization is clicked.\r
+ * \r
+ * Hides or shows the input fields for user & password. \r
+ * \r
+ * @param view 'View password' 'button'\r
+ */\r
+ public void onCheckClick(View view) {\r
+ CheckBox oAuth2Check = (CheckBox)view; \r
+ changeViewByOAuth2Check(oAuth2Check.isChecked());\r
+\r
+ }\r
+ \r
+ /**\r
+ * Changes the visibility of input elements depending upon the kind of authorization\r
+ * chosen by the user: basic or OAuth\r
+ * \r
+ * @param checked 'True' when OAuth is selected.\r
+ */\r
+ public void changeViewByOAuth2Check(Boolean checked) {\r
+ \r
+ if (checked) {\r
+ mOAuthAuthEndpointText.setVisibility(View.VISIBLE);\r
+ mOAuthTokenEndpointText.setVisibility(View.VISIBLE);\r
+ mUsernameInput.setVisibility(View.GONE);\r
+ mPasswordInput.setVisibility(View.GONE);\r
+ mViewPasswordButton.setVisibility(View.GONE);\r
+ } else {\r
+ mOAuthAuthEndpointText.setVisibility(View.GONE);\r
+ mOAuthTokenEndpointText.setVisibility(View.GONE);\r
+ mUsernameInput.setVisibility(View.VISIBLE);\r
+ mPasswordInput.setVisibility(View.VISIBLE);\r
+ mViewPasswordButton.setVisibility(View.INVISIBLE);\r
+ } \r
+\r
+ } \r
+ \r
+ /* Leave the old OAuth flow\r
+ // Results from the first call to oAuth2 server : getting the user_code and verification_url.\r
@Override\r
- public void onClick(View v) {\r
- if (v.getId() == R.id.refreshButton) {\r
- onFocusChange(findViewById(R.id.host_URL), false);\r
- } else if (v.getId() == R.id.viewPassword) {\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_PASSWORD;\r
- } else {\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
+ public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject responseJson) {\r
+ if ((type == ResultOAuthType.OK_SSL)||(type == ResultOAuthType.OK_NO_SSL)) {\r
+ mCodeResponseJson = responseJson;\r
+ if (mCodeResponseJson != null) {\r
+ getOAuth2AccessTokenFromJsonResponse();\r
+ } // else - nothing to do here - wait for callback !!!\r
+ \r
+ } else if (type == ResultOAuthType.HOST_NOT_AVAILABLE) {\r
+ updateOAuth2IconAndText(R.drawable.common_error, R.string.oauth_connection_url_unavailable);\r
}\r
}\r
\r
- @Override\r
- public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
- if (operation.equals(mConnChkRunnable)) {\r
- \r
- mStatusText = mStatusIcon = 0;\r
- mStatusCorrect = false;\r
- String t_url = ((TextView) findViewById(R.id.host_URL)).getText()\r
- .toString().trim().toLowerCase();\r
- \r
- switch (result.getCode()) {\r
- case OK_SSL:\r
- mIsSslConn = true;\r
- mStatusIcon = android.R.drawable.ic_secure;\r
- mStatusText = R.string.auth_secure_connection;\r
- mStatusCorrect = true;\r
- break;\r
- \r
- case OK_NO_SSL:\r
- case OK:\r
- mIsSslConn = false;\r
- mStatusCorrect = true;\r
- if (t_url.startsWith("http://") ) {\r
- mStatusText = R.string.auth_connection_established;\r
- mStatusIcon = R.drawable.ic_ok;\r
- } else {\r
- mStatusText = R.string.auth_nossl_plain_ok_title;\r
- mStatusIcon = android.R.drawable.ic_partial_secure;\r
- }\r
- break;\r
- \r
- \r
- case BAD_OC_VERSION:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_bad_oc_version_title;\r
- break;\r
- case WRONG_CONNECTION:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_wrong_connection_title;\r
- break;\r
- case TIMEOUT:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_timeout_title;\r
- break;\r
- case INCORRECT_ADDRESS:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_incorrect_address_title;\r
- break;\r
- \r
- case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_ssl_unverified_server_title;\r
- mLastSslUntrustedServerResult = result;\r
- showDialog(DIALOG_SSL_VALIDATOR); \r
- break;\r
- \r
- case SSL_ERROR:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_ssl_general_error_title;\r
- break;\r
- \r
- case HOST_NOT_AVAILABLE:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_unknown_host_title;\r
- break;\r
- case NO_NETWORK_CONNECTION:\r
- mStatusIcon = R.drawable.no_network;\r
- mStatusText = R.string.auth_no_net_conn_title;\r
- break;\r
- case INSTANCE_NOT_CONFIGURED:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_not_configured_title;\r
- break;\r
- case FILE_NOT_FOUND:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_incorrect_path_title;\r
- break;\r
- case UNHANDLED_HTTP_CODE:\r
- case UNKNOWN_ERROR:\r
- mStatusIcon = R.drawable.common_error;\r
- mStatusText = R.string.auth_unknown_error_title;\r
- break;\r
- default:\r
- Log.e(TAG, "Incorrect connection checker result type: " + result.getHttpCode());\r
- }\r
- setResultIconAndText(mStatusIcon, mStatusText);\r
- if (!mStatusCorrect)\r
- findViewById(R.id.refreshButton).setVisibility(View.VISIBLE);\r
- else\r
- findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);\r
- findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);\r
- }\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->" + mCodeResponseJson.toString());\r
+\r
+ try {\r
+ // We get data that we must show to the user or we will use internally.\r
+ verificationUrl = mCodeResponseJson.getString(OAuth2GetAuthorizationToken.CODE_VERIFICATION_URL);\r
+ userCode = mCodeResponseJson.getString(OAuth2GetAuthorizationToken.CODE_USER_CODE);\r
+ expiresIn = mCodeResponseJson.getInt(OAuth2GetAuthorizationToken.CODE_EXPIRES_IN); \r
+\r
+ // And we get data that we must use to get a token.\r
+ deviceCode = mCodeResponseJson.getString(OAuth2GetAuthorizationToken.CODE_DEVICE_CODE);\r
+ interval = mCodeResponseJson.getInt(OAuth2GetAuthorizationToken.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
+ updateOAuth2IconAndText(R.drawable.ic_ok, R.string.auth_connection_established);\r
+ \r
+ // Showing the dialog with instructions for the user.\r
+ showDialog(DIALOG_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
+ \r
+ /* Leave the old OAuth flow\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(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
+\r
+ }\r
+ }\r
+ */\r
+\r
+ \r
+ /**\r
+ * Called from SslValidatorDialog when a new server certificate was correctly saved.\r
+ */\r
public void onSavedCertificate() {\r
- mAuthThread = mConnChkRunnable.retry(this, mHandler); \r
+ mOperationThread = mOcServerChkOperation.retry(this, mHandler); \r
}\r
\r
+ /**\r
+ * Called from SslValidatorDialog when a new server certificate could not be saved \r
+ * when the user requested it.\r
+ */\r
@Override\r
public void onFailedSavingCertificate() {\r
showDialog(DIALOG_CERT_NOT_SAVED);\r
}\r
- \r
+\r
}\r
/* ownCloud Android client application\r
* Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
import android.content.IntentFilter;\r
import android.content.ServiceConnection;\r
import android.content.SharedPreferences;\r
+ import android.content.SharedPreferences.Editor;\r
import android.content.pm.PackageInfo;\r
import android.content.pm.PackageManager.NameNotFoundException;\r
import android.content.res.Resources.NotFoundException;\r
import com.owncloud.android.files.services.FileObserverService;\r
import com.owncloud.android.files.services.FileUploader;\r
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
-import com.owncloud.android.network.OwnCloudClientUtils;\r
+import com.owncloud.android.operations.CreateFolderOperation;\r
import com.owncloud.android.operations.OnRemoteOperationListener;\r
import com.owncloud.android.operations.RemoteOperation;\r
import com.owncloud.android.operations.RemoteOperationResult;\r
import com.owncloud.android.operations.SynchronizeFileOperation;\r
import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
import com.owncloud.android.syncadapter.FileSyncService;\r
+ import com.owncloud.android.ui.dialog.ChangelogDialog;\r
import com.owncloud.android.ui.dialog.SslValidatorDialog;\r
import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;\r
import com.owncloud.android.ui.fragment.FileDetailFragment;\r
import com.owncloud.android.ui.fragment.OCFileListFragment;\r
\r
import com.owncloud.android.R;\r
-import eu.alefzero.webdav.WebdavClient;\r
\r
/**\r
* Displays, what files the user has available in his ownCloud.\r
private OCFileListFragment mFileList;\r
\r
private boolean mDualPane;\r
+ private Handler mHandler;\r
\r
private static final int DIALOG_SETUP_ACCOUNT = 0;\r
private static final int DIALOG_CREATE_DIR = 1;\r
private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 4;\r
private static final int DIALOG_SSL_VALIDATOR = 5;\r
private static final int DIALOG_CERT_NOT_SAVED = 6;\r
+ private static final String DIALOG_CHANGELOG_TAG = "DIALOG_CHANGELOG";\r
\r
\r
private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;\r
public void onCreate(Bundle savedInstanceState) {\r
Log.d(getClass().toString(), "onCreate() start");\r
super.onCreate(savedInstanceState);\r
+ \r
+ mHandler = new Handler();\r
\r
/// Load of parameters from received intent\r
- mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); // no check necessary, mCurrenDir == null if the parameter is not in the intent\r
Account account = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);\r
- if (account != null)\r
- AccountUtils.setCurrentOwnCloudAccount(this, account.name);\r
+ if (account != null && AccountUtils.setCurrentOwnCloudAccount(this, account.name)) {\r
+ mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); \r
+ }\r
\r
/// Load of saved instance state: keep this always before initDataFromCurrentAccount()\r
if(savedInstanceState != null) {\r
// Drop-down navigation \r
mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);\r
OCFile currFile = mCurrentDir;\r
- while(currFile != null && currFile.getFileName() != OCFile.PATH_SEPARATOR) {\r
+ while(mStorageManager != null && currFile != null && currFile.getFileName() != OCFile.PATH_SEPARATOR) {\r
mDirectories.add(currFile.getFileName());\r
currFile = mStorageManager.getFileById(currFile.getParentId());\r
}\r
actionBar.setListNavigationCallbacks(mDirectories, this);\r
setSupportProgressBarIndeterminateVisibility(false); // always AFTER setContentView(...) ; to workaround bug in its implementation\r
\r
+ \r
+ // show changelog, if needed\r
+ //showChangeLog();\r
+ \r
Log.d(getClass().toString(), "onCreate() end");\r
}\r
\r
\r
/**\r
+ * Shows a dialog with the change log of the current version after each app update\r
+ * \r
+ * TODO make it permanent; by now, only to advice the workaround app for 4.1.x\r
+ */\r
+ private void showChangeLog() {\r
+ if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.JELLY_BEAN) {\r
+ final String KEY_VERSION = "version";\r
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());\r
+ int currentVersionNumber = 0;\r
+ int savedVersionNumber = sharedPref.getInt(KEY_VERSION, 0);\r
+ try {\r
+ PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);\r
+ currentVersionNumber = pi.versionCode;\r
+ } catch (Exception e) {}\r
+ \r
+ if (currentVersionNumber > savedVersionNumber) {\r
+ ChangelogDialog.newInstance(true).show(getSupportFragmentManager(), DIALOG_CHANGELOG_TAG);\r
+ Editor editor = sharedPref.edit();\r
+ editor.putInt(KEY_VERSION, currentVersionNumber);\r
+ editor.commit();\r
+ }\r
+ }\r
+ }\r
+ \r
+ \r
+ /**\r
* Launches the account creation activity. To use when no ownCloud account is available\r
*/\r
private void createFirstAccount() {\r
Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);\r
- intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });\r
+ intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTHORITY });\r
startActivity(intent); // the new activity won't be created until this.onStart() and this.onResume() are finished;\r
}\r
\r
}\r
\r
private void startSynchronization() {\r
- ContentResolver.cancelSync(null, AccountAuthenticator.AUTH_TOKEN_TYPE); // cancel the current synchronizations of any ownCloud account\r
+ ContentResolver.cancelSync(null, AccountAuthenticator.AUTHORITY); // cancel the current synchronizations of any ownCloud account\r
Bundle bundle = new Bundle();\r
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
ContentResolver.requestSync(\r
AccountUtils.getCurrentOwnCloudAccount(this),\r
- AccountAuthenticator.AUTH_TOKEN_TYPE, bundle);\r
+ AccountAuthenticator.AUTHORITY, bundle);\r
}\r
\r
\r
\r
// Create directory\r
path += directoryName + OCFile.PATH_SEPARATOR;\r
- Thread thread = new Thread(new DirectoryCreator(path, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler()));\r
- thread.start();\r
+ RemoteOperation operation = new CreateFolderOperation(path, mCurrentDir.getFileId(), mStorageManager);\r
+ operation.execute( AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), \r
+ FileDisplayActivity.this, \r
+ FileDisplayActivity.this, \r
+ mHandler,\r
+ FileDisplayActivity.this);\r
\r
dialog.dismiss();\r
\r
return !mDirectories.isEmpty();\r
}\r
\r
- private class DirectoryCreator implements Runnable {\r
- private String mTargetPath;\r
- private Account mAccount;\r
- private Handler mHandler; \r
- \r
- public DirectoryCreator(String targetPath, Account account, Handler handler) {\r
- mTargetPath = targetPath;\r
- mAccount = account;\r
- mHandler = handler;\r
- }\r
- \r
- @Override\r
- public void run() {\r
- WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());\r
- boolean created = wdc.createDirectory(mTargetPath);\r
- if (created) {\r
- mHandler.post(new Runnable() {\r
- @Override\r
- public void run() { \r
- dismissDialog(DIALOG_SHORT_WAIT);\r
- \r
- // Save new directory in local database\r
- OCFile newDir = new OCFile(mTargetPath);\r
- newDir.setMimetype("DIR");\r
- newDir.setParentId(mCurrentDir.getFileId());\r
- mStorageManager.saveFile(newDir);\r
- \r
- // Display the new folder right away\r
- mFileList.listDirectory();\r
- }\r
- });\r
- \r
- } else {\r
- mHandler.post(new Runnable() {\r
- @Override\r
- public void run() {\r
- dismissDialog(DIALOG_SHORT_WAIT);\r
- try {\r
- Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); \r
- msg.show();\r
- \r
- } catch (NotFoundException e) {\r
- Log.e(TAG, "Error while trying to show fail message " , e);\r
- }\r
- }\r
- });\r
- }\r
- }\r
- \r
- }\r
\r
// Custom array adapter to override text colors\r
private class CustomArrayAdapter<T> extends ArrayAdapter<T> {\r
\r
} else if (operation instanceof SynchronizeFileOperation) {\r
onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);\r
+ \r
+ } else if (operation instanceof CreateFolderOperation) {\r
+ onCreateFolderOperationFinish((CreateFolderOperation)operation, result);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Updates the view associated to the activity after the finish of an operation trying create a new folder\r
+ * \r
+ * @param operation Creation operation performed.\r
+ * @param result Result of the creation.\r
+ */\r
+ private void onCreateFolderOperationFinish(CreateFolderOperation operation, RemoteOperationResult result) {\r
+ if (result.isSuccess()) {\r
+ dismissDialog(DIALOG_SHORT_WAIT);\r
+ mFileList.listDirectory();\r
+ \r
+ } else {\r
+ dismissDialog(DIALOG_SHORT_WAIT);\r
+ try {\r
+ Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); \r
+ msg.show();\r
+ \r
+ } catch (NotFoundException e) {\r
+ Log.e(TAG, "Error while trying to show fail message " , e);\r
+ }\r
}\r
}\r
\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
dialog.dismiss();\r
switch (which) {\r
case DialogInterface.BUTTON_POSITIVE:\r
- Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");\r
+ Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);\r
intent.putExtra("authorities",\r
- new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });\r
+ new String[] { AccountAuthenticator.AUTHORITY });\r
startActivity(intent);\r
break;\r
case DialogInterface.BUTTON_NEGATIVE:\r
/* ownCloud Android client application\r
* Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
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
\r
import android.accounts.Account;\r
import android.accounts.AccountManager;\r
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
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.operations.OnRemoteOperationListener;\r
import com.owncloud.android.operations.RemoteOperation;\r
import com.owncloud.android.operations.RemoteOperationResult;\r
import com.owncloud.android.ui.activity.TransferServiceGetter;\r
import com.owncloud.android.ui.dialog.EditNameDialog;\r
import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;\r
-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
\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
boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
}\r
\r
// set file details\r
setFilename(mFile.getFileName());\r
- setFiletype(DisplayUtils.convertMIMEtoPrettyPrint(mFile\r
- .getMimetype()));\r
+ setFiletype(mFile.getMimetype());\r
setFilesize(mFile.getFileLength());\r
if(ocVersionSupportsTimeCreated()){\r
setTimeCreated(mFile.getCreationTimestamp());\r
*/\r
private void setFiletype(String mimetype) {\r
TextView tv = (TextView) getView().findViewById(R.id.fdType);\r
- if (tv != null)\r
- tv.setText(mimetype);\r
+ if (tv != null) {\r
+ String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);; \r
+ tv.setText(printableMimetype);\r
+ }\r
+ ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon);\r
+ if (iv != null) {\r
+ iv.setImageResource(DisplayUtils.getResourceId(mimetype));\r
+ }\r
}\r
\r
/**\r
if (mFile.getRemotePath().equals(uploadRemotePath) ||\r
renamedInUpload) {\r
if (uploadWasFine) {\r
- mFile = mStorageManager.getFileByPath(uploadRemotePath);\r
+ mFile = mStorageManager.getFileByPath(uploadRemotePath);\r
}\r
if (renamedInUpload) {\r
String newName = (new File(uploadRemotePath)).getName();\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 (operation instanceof RemoveFileOperation) {\r
- onRemoveFileOperationFinish((RemoveFileOperation)operation, result);\r
+ if (operation instanceof RemoveFileOperation) {\r
+ onRemoveFileOperationFinish((RemoveFileOperation)operation, result);\r
\r
- } else if (operation instanceof RenameFileOperation) {\r
- onRenameFileOperationFinish((RenameFileOperation)operation, result);\r
+ } else if (operation instanceof RenameFileOperation) {\r
+ onRenameFileOperationFinish((RenameFileOperation)operation, result);\r
\r
- } else if (operation instanceof SynchronizeFileOperation) {\r
- onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);\r
- }\r
+ } else if (operation instanceof SynchronizeFileOperation) {\r
+ onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);\r
}\r
}\r
\r
/* ownCloud Android client application
* Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
-import com.owncloud.android.network.OwnCloudClientUtils;
import com.owncloud.android.operations.OnRemoteOperationListener;
import com.owncloud.android.operations.RemoteOperation;
import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
-import eu.alefzero.webdav.WebdavClient;
import eu.alefzero.webdav.WebdavUtils;
import android.accounts.Account;
case R.id.download_file_item: {
Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity());
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(account, getSherlockActivity().getApplicationContext());
- operation.execute(wc, mContainerActivity, mHandler);
+ operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
getSherlockActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
return true;
}
AccountUtils.getCurrentOwnCloudAccount(getActivity()),
newFilename,
mContainerActivity.getStorageManager());
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
- operation.execute(wc, mContainerActivity, mHandler);
+ operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
}
}
RemoteOperation operation = new RemoveFileOperation( mTargetFile,
true,
mContainerActivity.getStorageManager());
- WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
- operation.execute(wc, mContainerActivity, mHandler);
+ operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
}
/* ownCloud Android client application\r
* Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
*\r
* This program is free software: you can redistribute it and/or modify\r
* it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
+ * the Free Software Foundation, either version 2 of the License, or\r
* (at your option) any later version.\r
*\r
* This program is distributed in the hope that it will be useful,\r
import java.io.FileOutputStream;\r
import java.io.IOException;\r
import java.io.InputStream;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
\r
import org.apache.commons.httpclient.Credentials;\r
+import org.apache.commons.httpclient.HostConfiguration;\r
import org.apache.commons.httpclient.HttpClient;\r
import org.apache.commons.httpclient.HttpConnectionManager;\r
import org.apache.commons.httpclient.HttpException;\r
+import org.apache.commons.httpclient.HttpMethod;\r
import org.apache.commons.httpclient.HttpMethodBase;\r
+import org.apache.commons.httpclient.HttpState;\r
import org.apache.commons.httpclient.HttpVersion;\r
import org.apache.commons.httpclient.UsernamePasswordCredentials;\r
+import org.apache.commons.httpclient.auth.AuthPolicy;\r
import org.apache.commons.httpclient.auth.AuthScope;\r
import org.apache.commons.httpclient.methods.GetMethod;\r
import org.apache.commons.httpclient.methods.HeadMethod;\r
import org.apache.http.params.CoreProtocolPNames;\r
import org.apache.jackrabbit.webdav.client.methods.DavMethod;\r
import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;\r
-import org.apache.jackrabbit.webdav.client.methods.MkColMethod;\r
+\r
+import com.owncloud.android.network.BearerAuthScheme;\r
+import com.owncloud.android.network.BearerCredentials;\r
\r
import android.net.Uri;\r
import android.util.Log;\r
getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);\r
}\r
\r
- public void setCredentials(String username, String password) {\r
- getParams().setAuthenticationPreemptive(true);\r
- getState().setCredentials(AuthScope.ANY,\r
- getCredentials(username, password));\r
+ public void setBearerCredentials(String accessToken) {\r
+ AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class);\r
+ \r
+ List<String> authPrefs = new ArrayList<String>(1);\r
+ authPrefs.add(BearerAuthScheme.AUTH_POLICY);\r
+ getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); \r
+ \r
+ mCredentials = new BearerCredentials(accessToken);\r
+ getState().setCredentials(AuthScope.ANY, mCredentials);\r
}\r
\r
- private Credentials getCredentials(String username, String password) {\r
- if (mCredentials == null)\r
- mCredentials = new UsernamePasswordCredentials(username, password);\r
- return mCredentials;\r
+ public void setBasicCredentials(String username, String password) {\r
+ List<String> authPrefs = new ArrayList<String>(1);\r
+ authPrefs.add(AuthPolicy.BASIC);\r
+ getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); \r
+ \r
+ getParams().setAuthenticationPreemptive(true);\r
+ mCredentials = new UsernamePasswordCredentials(username, password);\r
+ getState().setCredentials(AuthScope.ANY, mCredentials);\r
}\r
\r
+ \r
/**\r
* Downloads a file in remoteFilepath to the local targetPath.\r
* \r
return status;\r
}\r
\r
- /**\r
- * Creates a remote directory with the received path.\r
- * \r
- * @param path Path of the directory to create, URL DECODED\r
- * @return 'True' when the directory is successfully created\r
- */\r
- public boolean createDirectory(String path) {\r
- boolean result = false;\r
- int status = -1;\r
- MkColMethod mkcol = new MkColMethod(mUri.toString() + WebdavUtils.encodePath(path));\r
- try {\r
- Log.d(TAG, "Creating directory " + path);\r
- status = executeMethod(mkcol);\r
- Log.d(TAG, "Status returned: " + status);\r
- result = mkcol.succeeded();\r
- \r
- Log.d(TAG, "MKCOL to " + path + " finished with HTTP status " + status + (!result?"(FAIL)":""));\r
- exhaustResponse(mkcol.getResponseBodyAsStream());\r
- \r
- } catch (Exception e) {\r
- logException(e, "creating directory " + path);\r
- \r
- } finally {\r
- mkcol.releaseConnection(); // let the connection available for other methods\r
- }\r
- return result;\r
- }\r
- \r
\r
/**\r
* Check if a file exists in the OC server\r
public Uri getBaseUri() {\r
return mUri;\r
}\r
+ \r
+\r
+ @Override\r
+ public int executeMethod(HostConfiguration hostconfig, final HttpMethod method, final HttpState state) throws IOException, HttpException {\r
+ if (mCredentials instanceof BearerCredentials) {\r
+ method.getHostAuthState().setAuthScheme(AuthPolicy.getAuthScheme(BearerAuthScheme.AUTH_POLICY));\r
+ method.getHostAuthState().setAuthAttempted(true);\r
+ }\r
+ return super.executeMethod(hostconfig, method, state);\r
+ }\r
+\r
+ \r
+ public final Credentials getCredentials() {\r
+ return mCredentials;\r
+ }\r
\r
}\r