From: masensio Date: Tue, 1 Apr 2014 12:18:32 +0000 (+0200) Subject: Merge branch 'develop' into operations_service X-Git-Tag: oc-android-1.7.0_signed~345^2~14^2~1 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/5ac1a1ebbb78aaa7b85318bd7542b1c047291d19?hp=1f2b951824ebb14bc613fb625ceba6198902b238 Merge branch 'develop' into operations_service --- diff --git a/res/values/strings.xml b/res/values/strings.xml index a73f9061..65c0dbc2 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1,257 +1,269 @@ - %1$s Android App - version %1$s - Refresh account - Upload - Content from other apps - Files - Open with - Create directory - Settings - Details - Send - General - More - Accounts - Manage Accounts - App PIN - Protect your client - Enable instant uploads - Instantly upload photos taken by camera - Enable Logging - This is used to log problems - Logging History - This shows the recorded logs - Delete History - Help - Recommend to a friend - Feedback - Imprint - + %1$s Android App + version %1$s + Refresh account + Upload + Content from other apps + Files + Open with + Create directory + Settings + Details + Send + General + More + Accounts + Manage Accounts + App PIN + Protect your client + Enable instant uploads + Instantly upload photos taken by camera + Enable Logging + This is used to log problems + This shows the recorded logs + Delete History + Help + Recommend to a friend + Feedback + Imprint + "Try %1$s on your smartphone!" - "I want to invite you to use %1$s on your smartphone!\nDownload here: %2$s" + "I want to invite you to use %1$s on your smartphone!\nDownload here: %2$s" + - Check Server - Server address https://… - Username - Password - New to %1$s? - Files - Connect - Upload - Choose upload directory: - No account found - There are no %1$s accounts on your device. Please setup an account first. - Setup - Quit - No content to upload - No content was received. Nothing to upload. - %1$s is not allowed to access the shared content - Uploading - There are no files in this folder.\nNew files can be added with the \"Upload\" menu option. - Tap on a file to display additional information. - Size: - Type: - Created: - Modified: - Download - Refresh file - File was renamed to %1$s during upload - Share link - Unshare link - Yes - No - OK - Cancel download - Cancel upload - Cancel - Save & Exit - Error - Loading … - Unknown error - About - Change password - Delete account - Create account - Upload from … - Directory name - Uploading … - %1$d%% Uploading %2$s - Upload succeeded - %1$s was successfully uploaded - Upload failed - Upload of %1$s could not be completed - Downloading … - %1$d%% Downloading %2$s - Download succeeded - %1$s was successfully downloaded - Download failed - Download of %1$s could not be completed - Not downloaded yet - Choose account - Synchronization failed - Synchronization of %1$s could not be completed - Invalid password for %1$s + Check Server + Server address https://… + Username + Password + New to %1$s? + Files + Connect + Upload + Choose upload directory: + No account found + There are no %1$s accounts on your device. Please setup an account first. + + Setup + Quit + No content to upload + No content was received. Nothing to upload. + %1$s is not allowed to access the shared content + Uploading + There are no files in this folder.\nNew files can be added with the \"Upload\" + menu option. + Tap on a file to display additional information. + Size: + Type: + Created: + Modified: + Download + Refresh file + File was renamed to %1$s during upload + Share link + Unshare link + Yes + No + OK + Cancel download + Cancel upload + Cancel + Save & Exit + Error + Loading … + Unknown error + About + Change password + Delete account + Create account + Upload from … + Directory name + Uploading … + %1$d%% Uploading %2$s + Upload succeeded + %1$s was successfully uploaded + Upload failed + Upload of %1$s could not be completed + Downloading … + %1$d%% Downloading %2$s + Download succeeded + %1$s was successfully downloaded + Download failed + Download of %1$s could not be completed + Not downloaded yet + Choose account + Synchronization failed + Synchronization of %1$s could not be completed + Invalid password for %1$s Conflicts found %1$d kept-in-sync files could not be sync\'ed - Kept-in-sync files failed - Contents of %1$d files could not be sync\'ed (%2$d conflicts) - Some local files were forgotten - %1$d files out of the %2$s directory could not be copied into - As of version 1.3.16, files uploaded from this device are copied into the local %1$s folder to prevent data loss when a single file is synced with multiple accounts.\n\nDue to this change, all files uploaded in previous versions of this app were copied into the %2$s folder. However, an error prevented the completion of this operation during account synchronization. You may either leave the file(s) as is and remove the link to %3$s, or move the file(s) into the %1$s directory and retain the link to %4$s.\n\nListed below are the local file(s), and the remote file(s) in %5$s they were linked to. - Folder %1$s does not exist anymore - "Move all" - "All files were moved" - "Some files could not be moved" - "Local: %1$s" - "Remote: %1$s" - There is not space enough to copy the selected files into the %1$s folder. Would like to move them into instead? - Please, insert your App PIN - Enter your App PIN - The PIN will be requested every time the app is started - Please, reenter your App PIN - Remove your App PIN - The App PINs are not the same - Incorrect App PIN - App PIN removed - App PIN stored - - "%1$s music player" - "%1$s (playing)" - "%1$s (loading)" - "%1$s playback finished" - No media file found + Kept-in-sync files failed + Contents of %1$d files could not be sync\'ed (%2$d conflicts) + Some local files were forgotten + %1$d files out of the %2$s directory could not be copied into + As of version 1.3.16, files uploaded from this device are copied into the local + %1$s folder to prevent data loss when a single file is synced with multiple accounts.\n\nDue to + this change, all files uploaded in previous versions of this app were copied into the %2$s folder. + However, an error prevented the completion of this operation during account synchronization. You + may either leave the file(s) as is and remove the link to %3$s, or move the file(s) into the %1$s + directory and retain the link to %4$s.\n\nListed below are the local file(s), and the remote + file(s) in %5$s they were linked to. + Folder %1$s does not exist anymore + "Move all" + "All files were moved" + "Some files could not be moved" + "Local: %1$s" + "Remote: %1$s" + There is not space enough to copy the selected files into the %1$s folder. + Would like to move them into instead? + Please, insert your App PIN + Enter your App PIN + The PIN will be requested every time the app is started + Please, reenter your App PIN + Remove your App PIN + The App PINs are not the same + Incorrect App PIN + App PIN removed + App PIN stored + + "%1$s music player" + "%1$s (playing)" + "%1$s (loading)" + "%1$s playback finished" + No media file found No account provided - File not in a valid account - Unsupported media codec - Media file could not be read - Media file not correctly encoded - Timed out while trying to play - Media file cannot be streamed - Media file cannot be played with the stock media player - Security error trying to play %1$s + File not in a valid account + Unsupported media codec + Media file could not be read + Media file not correctly encoded + Timed out while trying to play + Media file cannot be streamed + Media file cannot be played with the stock media player + Security error trying to play %1$s Input error trying to play %1$s Unexpected error trying to play %1$s Rewind button Play or pause button Fast forward button - Trying to login… - No network connection - Secure connection unavailable. - Connection established - Testing connection… - Malformed server configuration - An account for the same user and server already exists in the device - The entered user does not match the user of this account - Unknown error occurred! - Couldn\'t find host - Server instance not found - The server took too long to respond - Malformed URL - SSL initialization failed - Couldn\'t verify SSL server\'s identity - Unrecognized server version - Couldn\'t establish connection - Secure connection established - Wrong username or password + Trying to login… + No network connection + Secure connection unavailable. + Connection established + Testing connection… + Malformed server configuration + An account for the same user and server already exists in the device + The entered user does not match the user of this account + Unknown error occurred! + Couldn\'t find host + Server instance not found + The server took too long to respond + Malformed URL + SSL initialization failed + Couldn\'t verify SSL server\'s identity + Unrecognized server version + Couldn\'t establish connection + Secure connection established + Wrong username or password Unsuccessful authorization Access denied by authorization server - Unexpected state; please, enter the server URL again - Your authorization expired. Please, authorize again - Please, enter the current password - Your session expired. Please connect again + Unexpected state; please, enter the server URL again + Your authorization expired. Please, authorize again + Please, enter the current password + Your session expired. Please connect again Connecting to authentication server… - The server does not support this authentication method + The server does not support this authentication method %1$s does not support multiple accounts - Your server is not returning a correct user id, please contact an administrator + Your server is not returning a correct user id, please contact an administrator + Can not authenticate against this server - - Keep file up to date - Rename - Remove - "Do you really want to remove %1$s ?" - "Do you really want to remove %1$s and its contents ?" - Local only - Local contents only - Remove from server - Remote and local - "Removal succeeded" - "Removal failed" - Enter a new name - "Local copy could not be renamed; try a different name" - "Rename could not be completed" - Remote file could not be checked - File contents already synchronized - Directory could not be created - Forbidden characters: / \\ < > : " | ? * - Wait a moment - "Unexpected problem ; please select the file from a different app" - No file was selected - Send link to … - - Login with oAuth2 - Connecting to oAuth2 server… - - The identity of the site could not be verified - - The server certificate is not trusted - - The server certificate expired - - The server certificate valid dates are in the future - - The URL does not match the hostname in the certificate - Do you want to trust this certificate anyway? - The certificate could not be saved - Details - Hide - Issued to: - Issued by: - Common name: - Organization: - Organizational unit: - Country: - State: - Location: - Validity: - From: + + Keep file up to date + Rename + Remove + "Do you really want to remove %1$s ?" + "Do you really want to remove %1$s and its contents ?" + Local only + Local contents only + Remove from server + Remote and local + "Removal succeeded" + "Removal failed" + Enter a new name + "Local copy could not be renamed; try a different name" + "Rename could not be completed" + Remote file could not be checked + File contents already synchronized + Directory could not be created + Forbidden characters: / \\ < > : " | ? * + Wait a moment + "Unexpected problem ; please select the file from a different app" + No file was selected + Send link to … + + Login with oAuth2 + Connecting to oAuth2 server… + + The identity of the site could not be verified + - The server certificate is not trusted + - The server certificate expired + - The server certificate valid dates are in the future + - The URL does not match the hostname in the certificate + Do you want to trust this certificate anyway? + The certificate could not be saved + Details + Hide + Issued to: + Issued by: + Common name: + Organization: + Organizational unit: + Country: + State: + Location: + Validity: + From: To: Signature: Algorithm: The certificate could not be shown. - No information about the error - - This is a placeholder - placeholder.txt - PNG Image - 389 KB - 2012/05/18 12:23 PM - 12:23:45 - - Upload pictures via WiFi only - /InstantUpload - Update conflict - Remote file %s is not synchronized with local file. Continuing will replace content of file on server. - Keep both - Overwrite - Don\'t upload - - Image preview - This image can not be shown - - %1$s could not be copied to %2$s local directory - Failed InstantUpload - Failed instant uploads - Summary of all failed instant uploads - select all - retry all selected - delete all selected from uploadqueue - retry to upload the image: - Load more Picrures - do nothing you are not online for instant upload + + This is a placeholder + placeholder.txt + PNG Image + 389 KB + 2012/05/18 12:23 PM + 12:23:45 + + Upload pictures via WiFi only + /InstantUpload + Update conflict + Remote file %s is not synchronized with local file. Continuing will replace + content of file on server. + Keep both + Overwrite + Don\'t upload + + Image preview + This image can not be shown + + %1$s could not be copied to %2$s local directory + Failed InstantUpload + Failed instant uploads + Summary of all failed instant uploads + select all + retry all selected + delete all selected from uploadqueue + retry to upload the image: + Load more Picrures + do nothing you are not online for instant upload Failure Message: Please check your server configuration,maybe your quota is exceeded. - - Sorry, sharing is not enabled on your server. Please contact your administrator. + + Sorry, sharing is not enabled on your server. Please contact your + administrator. Unable to share this file or folder. Please, make sure it exists An error occurred while trying to share this file or folder Unable to unshare this file or folder. It does not exist. @@ -259,7 +271,8 @@ Send - Copy link + Copy link Copied to clipboard - + + Critical error: can not perform operations diff --git a/src/com/owncloud/android/authentication/AuthenticatorActivity.java b/src/com/owncloud/android/authentication/AuthenticatorActivity.java index 8cbe7ad8..69eea77b 100644 --- a/src/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/src/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -25,8 +25,11 @@ import android.accounts.AccountManager; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; +import android.content.ComponentName; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.ServiceConnection; import android.content.SharedPreferences; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -34,6 +37,7 @@ import android.net.Uri; import android.net.http.SslError; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.preference.PreferenceManager; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; @@ -41,6 +45,7 @@ import android.support.v4.app.FragmentTransaction; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; +import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -54,6 +59,7 @@ import android.widget.CheckBox; import android.widget.EditText; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; +import android.widget.Toast; import com.actionbarsherlock.app.SherlockDialogFragment; import com.owncloud.android.MainApp; @@ -76,6 +82,8 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCo import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation; import com.owncloud.android.lib.resources.users.GetRemoteUserNameOperation; +import com.owncloud.android.services.OperationsService; +import com.owncloud.android.services.OperationsService.OperationsServiceBinder; import com.owncloud.android.ui.dialog.SamlWebViewDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener; @@ -116,6 +124,9 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { private static final String KEY_AUTH_STATUS_ICON = "AUTH_STATUS_ICON"; private static final String KEY_REFRESH_BUTTON_ENABLED = "KEY_REFRESH_BUTTON_ENABLED"; //private static final String KEY_IS_SHARED_SUPPORTED = "KEY_IS_SHARE_SUPPORTED"; + private static final String KEY_SERVER_AUTH_METHOD = "KEY_SERVER_AUTH_METHOD"; + private static final String KEY_DETECT_AUTH_OP_ID = "KEY_DETECT_AUTH_OP_ID"; + private static final String AUTH_ON = "on"; private static final String AUTH_OFF = "off"; @@ -136,6 +147,9 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { private String mAuthMessageText; private int mAuthMessageVisibility, mServerStatusText, mServerStatusIcon; private boolean mServerIsChecked, mServerIsValid, mIsSslConn; + private AuthenticationMethod mServerAuthMethod = AuthenticationMethod.UNKNOWN; + private int mDetectAuthOpId = -1; + private int mAuthStatusText, mAuthStatusIcon; private TextView mAuthStatusLayout; @@ -176,9 +190,10 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { private boolean mResumed; // Control if activity is resumed public static String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT"; - - private DetectAuthenticationMethodOperation mDetectAuthenticationOperation; - + + private ServiceConnection mOperationsServiceConnection = null; + + private OperationsServiceBinder mOperationsServiceBinder = null; /** * {@inheritDoc} @@ -190,6 +205,18 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_NO_TITLE); + // bind to Operations Service + mOperationsServiceConnection = new OperationsServiceConnection(); + if (!bindService(new Intent(this, OperationsService.class), + mOperationsServiceConnection, + Context.BIND_AUTO_CREATE)) { + Toast.makeText(this, + R.string.error_cant_bind_to_operations_service, + Toast.LENGTH_LONG) + .show(); + finish(); + } + /// set view and get references to view elements setContentView(R.layout.account_setup); mAuthMessage = (TextView) findViewById(R.id.auth_message); @@ -306,6 +333,10 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { refreshButtonEnabled = savedInstanceState.getBoolean(KEY_REFRESH_BUTTON_ENABLED); + mServerAuthMethod = AuthenticationMethod.valueOf( + savedInstanceState.getString(KEY_SERVER_AUTH_METHOD)); + mDetectAuthOpId = savedInstanceState.getInt(KEY_DETECT_AUTH_OP_ID); + } if (mAuthMessageVisibility== View.VISIBLE) { @@ -391,6 +422,7 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { return false; } }); + } @@ -443,6 +475,7 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { */ @Override protected void onSaveInstanceState(Bundle outState) { + //Log.wtf(TAG, "onSaveInstanceState init" ); super.onSaveInstanceState(outState); /// connection state and info @@ -472,8 +505,10 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { // refresh button enabled outState.putBoolean(KEY_REFRESH_BUTTON_ENABLED, (mRefreshButton.getVisibility() == View.VISIBLE)); - - + + outState.putString(KEY_SERVER_AUTH_METHOD, mServerAuthMethod.name()); + outState.putInt(KEY_DETECT_AUTH_OP_ID, mDetectAuthOpId); + //Log.wtf(TAG, "onSaveInstanceState end" ); } @@ -500,7 +535,9 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { */ @Override protected void onResume() { + //Log.wtf(TAG, "onResume init" ); super.onResume(); + if (mAction == ACTION_UPDATE_TOKEN && mJustCreated && getIntent().getBooleanExtra(EXTRA_ENFORCED_UPDATE, false)) { if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) { //Toast.makeText(this, R.string.auth_expired_oauth_token_toast, Toast.LENGTH_LONG).show(); @@ -517,9 +554,35 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { if (mNewCapturedUriFromOAuth2Redirection != null) { getOAuth2AccessTokenFromCapturedRedirection(); } - + mJustCreated = false; + if (mOperationsServiceBinder != null) { + doOnResumeAndBound(); + } + + //Log.wtf(TAG, "onResume end" ); + } + + + @Override + protected void onPause() { + //Log.wtf(TAG, "onPause init" ); + if (mOperationsServiceBinder != null) { + //Log.wtf(TAG, "unregistering to listen for operation callbacks" ); + mOperationsServiceBinder.removeOperationListener(this); + } + super.onPause(); + //Log.wtf(TAG, "onPause end" ); + } + + @Override + protected void onDestroy() { + if (mOperationsServiceConnection != null) { + unbindService(mOperationsServiceConnection); + mOperationsServiceBinder = null; + } + super.onDestroy(); } @@ -811,13 +874,15 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { onGetUserNameFinish((GetRemoteUserNameOperation) operation, result); } else if (operation instanceof DetectAuthenticationMethodOperation) { - onDetectAutheticationFinish((DetectAuthenticationMethodOperation) operation, result); + Log.wtf(TAG, "received detection response through callback" ); + onDetectAuthenticationFinish(result); } } - private void onDetectAutheticationFinish(DetectAuthenticationMethodOperation operation, RemoteOperationResult result) { + private void onDetectAuthenticationFinish(RemoteOperationResult result) { // Read authentication method + mDetectAuthOpId = -1; if (result.getData().size() > 0) { AuthenticationMethod authMethod = (AuthenticationMethod) result.getData().get(0); String basic = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType()); @@ -959,14 +1024,23 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { private void detectAuthorizationMethod() { Log_OC.d(TAG, "Trying empty authorization to detect authentication method"); - - /// get the path to the root folder through WebDAV from the version server + String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType); - + /// test credentials - mDetectAuthenticationOperation = new DetectAuthenticationMethodOperation(this); - OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true); - mOperationThread = mDetectAuthenticationOperation.execute(client, this, mHandler); + //Intent detectAuthIntent = new Intent(this, OperationsService.class); + Intent detectAuthIntent = new Intent(); + detectAuthIntent.setAction(OperationsService.ACTION_DETECT_AUTHENTICATION_METHOD); + detectAuthIntent.putExtra(OperationsService.EXTRA_SERVER_URL, mHostBaseUrl); + detectAuthIntent.putExtra(OperationsService.EXTRA_WEBDAV_PATH, webdav_path); + + //if (mOperationsBinder != null) { // let's let it crash to detect if is really possible + mServerAuthMethod = AuthenticationMethod.UNKNOWN; + if (mOperationsServiceBinder != null) { + //Log.wtf(TAG, "starting detection..." ); + mDetectAuthOpId = mOperationsServiceBinder.newOperation(detectAuthIntent); + } + //} } @@ -1790,5 +1864,49 @@ SsoWebViewClientListener, OnSslUntrustedCertListener { } } + + + private void doOnResumeAndBound() { + //Log.wtf(TAG, "registering to listen for operation callbacks" ); + mOperationsServiceBinder.addOperationListener(AuthenticatorActivity.this, mHandler); + + if (mDetectAuthOpId != -1) { + RemoteOperationResult result = + mOperationsServiceBinder.getOperationResultIfFinished(mDetectAuthOpId); + if (result != null) { + //Log.wtf(TAG, "found result of operation finished while rotating"); + onDetectAuthenticationFinish(result); + } + } + } + + /** + * Implements callback methods for service binding. + */ + private class OperationsServiceConnection implements ServiceConnection { + + @Override + public void onServiceConnected(ComponentName component, IBinder service) { + if (component.equals(new ComponentName(AuthenticatorActivity.this, OperationsService.class))) { + //Log_OC.wtf(TAG, "Operations service connected"); + mOperationsServiceBinder = (OperationsServiceBinder) service; + + doOnResumeAndBound(); + + } else { + return; + } + + } + @Override + public void onServiceDisconnected(ComponentName component) { + if (component.equals(new ComponentName(AuthenticatorActivity.this, OperationsService.class))) { + Log_OC.e(TAG, "Operations service crashed"); + mOperationsServiceBinder = null; + } + } + + } + } diff --git a/src/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java b/src/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java index c6703161..6bc87503 100644 --- a/src/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java +++ b/src/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java @@ -64,14 +64,17 @@ public class DetectAuthenticationMethodOperation extends RemoteOperation { } private Context mContext; + private String mWebDavUrl; /** * Constructor * * @param context Android context of the caller. + * @param webdavUrl */ - public DetectAuthenticationMethodOperation(Context context) { + public DetectAuthenticationMethodOperation(Context context, String webdavUrl) { mContext = context; + mWebDavUrl = webdavUrl; } @@ -90,6 +93,7 @@ public class DetectAuthenticationMethodOperation extends RemoteOperation { AuthenticationMethod authMethod = AuthenticationMethod.UNKNOWN; RemoteOperation operation = new ExistenceCheckRemoteOperation("", mContext, false); + client.setWebdavUri(Uri.parse(mWebDavUrl)); client.setBasicCredentials("", ""); client.setFollowRedirects(false); diff --git a/src/com/owncloud/android/services/OperationsService.java b/src/com/owncloud/android/services/OperationsService.java index 80caea71..0ccd9094 100644 --- a/src/com/owncloud/android/services/OperationsService.java +++ b/src/com/owncloud/android/services/OperationsService.java @@ -1,5 +1,5 @@ /* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. + * Copyright (C) 2012-2014 ownCloud Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -18,10 +18,10 @@ package com.owncloud.android.services; import java.io.IOException; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.lib.common.OwnCloudClientFactory; @@ -32,6 +32,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.operations.common.SyncOperation; import com.owncloud.android.operations.CreateShareOperation; +import com.owncloud.android.operations.DetectAuthenticationMethodOperation; import com.owncloud.android.operations.UnshareLinkOperation; import com.owncloud.android.utils.Log_OC; @@ -47,7 +48,6 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Process; -//import android.support.v4.content.LocalBroadcastManager; import android.util.Pair; public class OperationsService extends Service { @@ -59,14 +59,20 @@ public class OperationsService extends Service { public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH"; public static final String EXTRA_SEND_INTENT = "SEND_INTENT"; public static final String EXTRA_RESULT = "RESULT"; + public static final String EXTRA_WEBDAV_PATH = "WEBDAV_PATH"; public static final String ACTION_CREATE_SHARE = "CREATE_SHARE"; public static final String ACTION_UNSHARE = "UNSHARE"; + public static final String ACTION_DETECT_AUTHENTICATION_METHOD = "DETECT_AUTHENTICATION_METHOD"; public static final String ACTION_OPERATION_ADDED = OperationsService.class.getName() + ".OPERATION_ADDED"; public static final String ACTION_OPERATION_FINISHED = OperationsService.class.getName() + ".OPERATION_FINISHED"; - private ConcurrentLinkedQueue> mPendingOperations = new ConcurrentLinkedQueue>(); + private ConcurrentLinkedQueue> mPendingOperations = + new ConcurrentLinkedQueue>(); + + private ConcurrentMap mOperationResults = + new ConcurrentHashMap(); private static class Target { public Uri mServerUrl = null; @@ -99,6 +105,7 @@ public class OperationsService extends Service { mBinder = new OperationsServiceBinder(); } + /** * Entry point to add a new operation to the queue of operations. * @@ -110,47 +117,11 @@ public class OperationsService extends Service { */ @Override public int onStartCommand(Intent intent, int flags, int startId) { - if (!intent.hasExtra(EXTRA_ACCOUNT) && !intent.hasExtra(EXTRA_SERVER_URL)) { - Log_OC.e(TAG, "Not enough information provided in intent"); - return START_NOT_STICKY; - } - try { - Account account = intent.getParcelableExtra(EXTRA_ACCOUNT); - String serverUrl = intent.getStringExtra(EXTRA_SERVER_URL); - - Target target = new Target(account, (serverUrl == null) ? null : Uri.parse(serverUrl)); - RemoteOperation operation = null; - - String action = intent.getAction(); - if (action.equals(ACTION_CREATE_SHARE)) { // Create Share - String remotePath = intent.getStringExtra(EXTRA_REMOTE_PATH); - Intent sendIntent = intent.getParcelableExtra(EXTRA_SEND_INTENT); - if (remotePath.length() > 0) { - operation = new CreateShareOperation(remotePath, ShareType.PUBLIC_LINK, - "", false, "", 1, sendIntent); - } - } else if (action.equals(ACTION_UNSHARE)) { // Unshare file - String remotePath = intent.getStringExtra(EXTRA_REMOTE_PATH); - if (remotePath.length() > 0) { - operation = new UnshareLinkOperation(remotePath, this.getApplicationContext()); - } - } else { - // nothing we are going to handle - return START_NOT_STICKY; - } - - mPendingOperations.add(new Pair(target, operation)); - //sendBroadcastNewOperation(target, operation); - - Message msg = mServiceHandler.obtainMessage(); - msg.arg1 = startId; - mServiceHandler.sendMessage(msg); - - } catch (IllegalArgumentException e) { - Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage()); - return START_NOT_STICKY; - } - + //Log.wtf(TAG, "onStartCommand init" ); + Message msg = mServiceHandler.obtainMessage(); + msg.arg1 = startId; + mServiceHandler.sendMessage(msg); + //Log.wtf(TAG, "onStartCommand end" ); return START_NOT_STICKY; } @@ -161,6 +132,7 @@ public class OperationsService extends Service { */ @Override public IBinder onBind(Intent intent) { + //Log.wtf(TAG, "onBind" ); return mBinder; } @@ -170,7 +142,7 @@ public class OperationsService extends Service { */ @Override public boolean onUnbind(Intent intent) { - //((OperationsServiceBinder)mBinder).clearListeners(); + ((OperationsServiceBinder)mBinder).clearListeners(); return false; // not accepting rebinding (default behaviour) } @@ -185,7 +157,8 @@ public class OperationsService extends Service { /** * Map of listeners that will be reported about the end of operations from a {@link OperationsServiceBinder} instance */ - private Map mBoundListeners = new HashMap(); + private ConcurrentMap mBoundListeners = + new ConcurrentHashMap(); /** * Cancels an operation @@ -210,7 +183,9 @@ public class OperationsService extends Service { * @param callbackHandler {@link Handler} to access the listener without breaking Android threading protection. */ public void addOperationListener (OnRemoteOperationListener listener, Handler callbackHandler) { - mBoundListeners.put(listener, callbackHandler); + synchronized (mBoundListeners) { + mBoundListeners.put(listener, callbackHandler); + } } @@ -220,7 +195,9 @@ public class OperationsService extends Service { * @param listener Object to notify about progress of transfer. */ public void removeOperationListener (OnRemoteOperationListener listener) { - mBoundListeners.remove(listener); + synchronized (mBoundListeners) { + mBoundListeners.remove(listener); + } } @@ -233,6 +210,74 @@ public class OperationsService extends Service { return (!mPendingOperations.isEmpty()); } + + /** + * Creates and adds to the queue a new operation, as described by operationIntent + * + * @param operationIntent Intent describing a new operation to queue and execute. + * @return Identifier of the operation created, or -1 if failed. + */ + public int newOperation(Intent operationIntent) { + RemoteOperation operation = null; + Target target = null; + try { + if (!operationIntent.hasExtra(EXTRA_ACCOUNT) && + !operationIntent.hasExtra(EXTRA_SERVER_URL)) { + Log_OC.e(TAG, "Not enough information provided in intent"); + + } else { + Account account = operationIntent.getParcelableExtra(EXTRA_ACCOUNT); + String serverUrl = operationIntent.getStringExtra(EXTRA_SERVER_URL); + target = new Target( + account, + (serverUrl == null) ? null : Uri.parse(serverUrl) + ); + + String action = operationIntent.getAction(); + if (action.equals(ACTION_CREATE_SHARE)) { // Create Share + String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); + Intent sendIntent = operationIntent.getParcelableExtra(EXTRA_SEND_INTENT); + if (remotePath.length() > 0) { + operation = new CreateShareOperation(remotePath, ShareType.PUBLIC_LINK, + "", false, "", 1, sendIntent); + } + } else if (action.equals(ACTION_UNSHARE)) { // Unshare file + String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); + if (remotePath.length() > 0) { + operation = new UnshareLinkOperation( + remotePath, + OperationsService.this); + } + } else if (action.equals(ACTION_DETECT_AUTHENTICATION_METHOD)) { + // Detect Authentication Method + String webdav_url = + serverUrl + operationIntent.getStringExtra(EXTRA_WEBDAV_PATH); + operation = new DetectAuthenticationMethodOperation( + OperationsService.this, + webdav_url); + } + } + + } catch (IllegalArgumentException e) { + Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage()); + operation = null; + } + + if (operation != null) { + mPendingOperations.add(new Pair(target, operation)); + startService(new Intent(OperationsService.this, OperationsService.class)); + return operation.hashCode(); + + } else { + return -1; + } + } + + public RemoteOperationResult getOperationResultIfFinished(int mDetectAuthOpId) { + //Log_OC.wtf(TAG, "Searching result for operation with id " + mDetectAuthOpId); + return mOperationResults.remove(mDetectAuthOpId); + } + } @@ -265,6 +310,8 @@ public class OperationsService extends Service { */ private void nextOperation() { + //Log.wtf(TAG, "nextOperation init" ); + Pair next = null; synchronized(mPendingOperations) { next = mPendingOperations.peek(); @@ -320,6 +367,7 @@ public class OperationsService extends Service { } finally { synchronized(mPendingOperations) { mPendingOperations.poll(); + mOperationResults.put(mCurrentOperation.hashCode(), result); } } @@ -382,7 +430,9 @@ public class OperationsService extends Service { * @param operation Finished operation. * @param result Result of the operation. */ - private void callbackOperationListeners(Target target, final RemoteOperation operation, final RemoteOperationResult result) { + private void callbackOperationListeners( + Target target, final RemoteOperation operation, final RemoteOperationResult result) { + int count = 0; Iterator listeners = mBinder.mBoundListeners.keySet().iterator(); while (listeners.hasNext()) { final OnRemoteOperationListener listener = listeners.next(); @@ -394,9 +444,10 @@ public class OperationsService extends Service { listener.onRemoteOperationFinish(operation, result); } }); + count += 1; } } - + Log_OC.d(TAG, "Called " + count + " listeners"); } diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java index 42b90aec..c4dd28a2 100644 --- a/src/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/com/owncloud/android/ui/activity/FileActivity.java @@ -150,22 +150,26 @@ public class FileActivity extends SherlockFragmentActivity implements OnRemoteOp @Override protected void onStart() { - super.onStart(); + if (mAccountWasSet) { onAccountSet(mAccountWasRestored); } if (mOperationsServiceBinder != null) { mOperationsServiceBinder.addOperationListener(FileActivity.this, mHandler); } + + super.onStart(); } @Override protected void onStop() { - super.onStop(); + if (mOperationsServiceBinder != null) { mOperationsServiceBinder.removeOperationListener(this); } + + super.onStop(); } diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 867e4cfe..f6136ed3 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -19,6 +19,7 @@ package com.owncloud.android.ui.activity; import java.io.File; + import android.accounts.Account; import android.app.AlertDialog; import android.app.Dialog;