diff --git a/src/java/JavaFileStorage/JavaFileStorage.iml b/src/java/JavaFileStorage/JavaFileStorage.iml index 3182901a..e0291e9b 100644 --- a/src/java/JavaFileStorage/JavaFileStorage.iml +++ b/src/java/JavaFileStorage/JavaFileStorage.iml @@ -8,7 +8,7 @@ - + diff --git a/src/java/JavaFileStorage/app/app.iml b/src/java/JavaFileStorage/app/app.iml index dab87834..2b187eb2 100644 --- a/src/java/JavaFileStorage/app/app.iml +++ b/src/java/JavaFileStorage/app/app.iml @@ -12,8 +12,9 @@ - + - + @@ -47,6 +48,13 @@ + + + + + + + @@ -61,31 +69,30 @@ - + + + + + + + - - - - - - + - - - - - + - + + + @@ -96,15 +103,19 @@ + + + + - - + - + + \ No newline at end of file diff --git a/src/java/JavaFileStorage/app/build.gradle b/src/java/JavaFileStorage/app/build.gradle index b6578585..0874801c 100644 --- a/src/java/JavaFileStorage/app/build.gradle +++ b/src/java/JavaFileStorage/app/build.gradle @@ -2,9 +2,9 @@ apply plugin: 'com.android.library' android { compileSdkVersion 23 - buildToolsVersion '23.0.0' + buildToolsVersion '23.0.2' defaultConfig { - minSdkVersion 14 + minSdkVersion 15 targetSdkVersion 23 } buildTypes { @@ -18,23 +18,61 @@ android { } dependencies { - compile 'com.google.http-client:google-http-client-gson:1.20.0' - compile 'com.google.code.gson:gson:2.1' - compile files('libs/commons-logging-1.1.1.jar') - compile files('libs/dropbox-android-sdk-1.6.2.jar') - compile files('libs/google-api-client-1.16.0-rc.jar') - compile files('libs/google-api-client-android-1.16.0-rc.jar') - compile files('libs/google-api-services-drive-v2-rev102-1.16.0-rc.jar') - compile files('libs/google-http-client-1.16.0-rc.jar') - compile files('libs/google-http-client-android-1.16.0-rc.jar') - compile files('libs/google-http-client-jackson-1.16.0-rc.jar') - compile files('libs/google-http-client-jackson2-1.16.0-rc.jar') - compile files('libs/google-oauth-client-1.16.0-rc.jar') - compile files('libs/httpclient-4.0.3.jar') - compile files('libs/httpcore-4.0.1.jar') - compile files('libs/httpmime-4.0.3.jar') - compile files('libs/jackson-core-2.1.3.jar') - compile files('libs/jackson-core-asl-1.9.11.jar') - compile files('libs/json_simple-1.1.jar') - compile files('libs/jsr305-1.3.9.jar') + /* + //compile files('libs/google-api-services-drive-v2-rev102-1.16.0-rc') + compile 'com.google.android.gms:play-services:6.5.+' + compile 'com.google.api-client:google-api-client-xml:1.18.0-rc' + compile 'com.google.http-client:google-http-client-gson:1.18.0-rc' + compile 'com.google.api-client:google-api-client-android:1.18.0-rc' + compile 'com.google.apis:google-api-services-drive:v2-rev155-1.19.0' +*/ + compile 'com.squareup.okhttp3:okhttp:3.4.1' + compile 'com.burgstaller:okhttp-digest:1.7' + +// compile files('libs/dropbox-android-sdk-1.6.2.jar') + + compile 'com.google.android.gms:play-services:4.0.30' + + compile('com.google.api-client:google-api-client-xml:1.17.0-rc') { + exclude group: 'com.google.android.google-play-services' + } + compile 'com.google.http-client:google-http-client-gson:1.17.0-rc' + compile('com.google.api-client:google-api-client-android:1.17.0-rc') { + exclude group: 'com.google.android.google-play-services' + } + compile 'com.google.apis:google-api-services-drive:v2-rev105-1.17.0-rc' + + //compile 'com.dropbox.core:dropbox-core-sdk:2.0.1' + //compile group: 'com.dropbox.core', name: 'dropbox-core-sdk', version: '0-SNAPSHOT', changing: true + + compile 'com.dropbox.core:dropbox-core-sdk:2.1.1' + + + //onedrive: + compile ('com.onedrive.sdk:onedrive-sdk-android:1.2+') { + transitive = false + } + // Include the gson dependency + compile ('com.google.code.gson:gson:2.3.1') + compile ('com.microsoft.services.msa:msa-auth:0.8.+') + compile ('com.microsoft.aad:adal:1.1.+') + + /* compile 'com.google.http-client:google-http-client-gson:1.20.0' + compile 'com.google.code.gson:gson:2.1' + compile files('libs/commons-logging-1.1.1.jar') + compile files('libs/dropbox-android-sdk-1.6.2.jar') + compile files('libs/google-api-client-1.16.0-rc.jar') + compile files('libs/google-api-client-android-1.16.0-rc.jar') + compile files('libs/google-http-client-1.16.0-rc.jar') + compile files('libs/google-http-client-android-1.16.0-rc.jar') + compile files('libs/google-http-client-jackson-1.16.0-rc.jar') + compile files('libs/google-http-client-jackson2-1.16.0-rc.jar') + compile files('libs/google-oauth-client-1.16.0-rc.jar') + compile files('libs/httpclient-4.0.3.jar') + compile files('libs/httpcore-4.0.1.jar') + compile files('libs/httpmime-4.0.3.jar') + compile files('libs/jackson-core-2.1.3.jar') + compile files('libs/jackson-core-asl-1.9.11.jar') + compile files('libs/json_simple-1.1.jar') + compile files('libs/jsr305-1.3.9.jar')*/ } diff --git a/src/java/JavaFileStorage/app/build/outputs/aar/app-debug.aar b/src/java/JavaFileStorage/app/build/outputs/aar/app-debug.aar deleted file mode 100644 index efb541d1..00000000 Binary files a/src/java/JavaFileStorage/app/build/outputs/aar/app-debug.aar and /dev/null differ diff --git a/src/java/JavaFileStorage/app/proguard-project.txt b/src/java/JavaFileStorage/app/proguard-project.txt index f2fe1559..d420505d 100644 --- a/src/java/JavaFileStorage/app/proguard-project.txt +++ b/src/java/JavaFileStorage/app/proguard-project.txt @@ -18,3 +18,17 @@ #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} + +-verbose +-dontobfuscate + +-keep class keepass2android.javafilestorage.** {*; } +-keep class keepass2android.javafilestorage.JavaFileStorage {*; } +-keep class keepass2android.javafilestorage.JavaFileStorage$* {*; } +-keep interface keepass2android.javafilestorage.JavaFileStorage$* {*; } +-keep class keepass2android.javafilestorage.JavaFileStorage$FileEntry {*; } +-keepclassmembers class keepass2android.javafilestorage.JavaFileStorage {*; } + +-keep interface keepass2android.javafilestorage.** {*; } + +-keep interface keepass2android.javafilestorage.JavaFileStorage$FileStorageSetupActivity {*; } \ No newline at end of file diff --git a/src/java/JavaFileStorage/app/src/main/AndroidManifest.xml b/src/java/JavaFileStorage/app/src/main/AndroidManifest.xml index 5cad9c93..b22b9aac 100644 --- a/src/java/JavaFileStorage/app/src/main/AndroidManifest.xml +++ b/src/java/JavaFileStorage/app/src/main/AndroidManifest.xml @@ -1,23 +1,15 @@ + android:versionName="1.0"> - - - - - - + diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/AccessTokenRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/AccessTokenRequest.java deleted file mode 100644 index 9d98e4da..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/AccessTokenRequest.java +++ /dev/null @@ -1,77 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.util.List; - -import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; -import org.apache.http.message.BasicNameValuePair; - -import android.text.TextUtils; - -import com.microsoft.live.OAuth.GrantType; - -/** - * AccessTokenRequest represents a request for an Access Token. - * It subclasses the abstract class TokenRequest, which does most of the work. - * This class adds the proper parameters for the access token request via the - * constructBody() hook. - */ -class AccessTokenRequest extends TokenRequest { - - /** - * REQUIRED. The authorization code received from the - * authorization server. - */ - private final String code; - - /** REQUIRED. Value MUST be set to "authorization_code". */ - private final GrantType grantType; - - /** - * REQUIRED, if the "redirect_uri" parameter was included in the - * authorization request as described in Section 4.1.1, and their - * values MUST be identical. - */ - private final String redirectUri; - - /** - * Constructs a new AccessTokenRequest, and initializes its member variables - * - * @param client the HttpClient to make HTTP requests on - * @param clientId the client_id of the calling application - * @param redirectUri the redirect_uri to be called back - * @param code the authorization code received from the AuthorizationRequest - */ - public AccessTokenRequest(HttpClient client, - String clientId, - String redirectUri, - String code) { - super(client, clientId); - - assert !TextUtils.isEmpty(redirectUri); - assert !TextUtils.isEmpty(code); - - this.redirectUri = redirectUri; - this.code = code; - this.grantType = GrantType.AUTHORIZATION_CODE; - } - - /** - * Adds the "code", "redirect_uri", and "grant_type" parameters to the body. - * - * @param body the list of NameValuePairs to be placed in the body of the HTTP request - */ - @Override - protected void constructBody(List body) { - body.add(new BasicNameValuePair(OAuth.CODE, this.code)); - body.add(new BasicNameValuePair(OAuth.REDIRECT_URI, this.redirectUri)); - body.add(new BasicNameValuePair(OAuth.GRANT_TYPE, - this.grantType.toString().toLowerCase())); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ApiRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ApiRequest.java deleted file mode 100644 index 7cf6cc92..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ApiRequest.java +++ /dev/null @@ -1,252 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.auth.AUTH; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.message.BasicHeader; -import org.json.JSONException; -import org.json.JSONObject; - -import android.net.Uri; -import android.os.Build; -import android.text.TextUtils; - -/** - * ApiRequest is an abstract base class that represents an Http Request made by the API. - * It does most of the Http Request work inside of the execute method, and provides a - * an abstract factory method for subclasses to choose the type of Http Request to be - * created. - */ -abstract class ApiRequest { - - public interface Observer { - public void onComplete(HttpResponse response); - } - - public enum Redirects { - SUPPRESS { - @Override - protected void setQueryParameterOn(UriBuilder builder) { - Redirects.setQueryParameterOn(builder, Boolean.TRUE); - } - }, UNSUPPRESSED { - @Override - protected void setQueryParameterOn(UriBuilder builder) { - Redirects.setQueryParameterOn(builder, Boolean.FALSE); - } - }; - - /** - * Sets the suppress_redirects query parameter by removing all existing ones - * and then appending it on the given UriBuilder. - */ - protected abstract void setQueryParameterOn(UriBuilder builder); - - private static void setQueryParameterOn(UriBuilder builder, Boolean value) { - // The Live SDK is designed to use our value of suppress_redirects. - // If it uses any other value it could cause issues. Remove any previously - // existing suppress_redirects and use ours. - builder.removeQueryParametersWithKey(QueryParameters.SUPPRESS_REDIRECTS); - builder.appendQueryParameter(QueryParameters.SUPPRESS_REDIRECTS, value.toString()); - } - } - - public enum ResponseCodes { - SUPPRESS { - @Override - protected void setQueryParameterOn(UriBuilder builder) { - ResponseCodes.setQueryParameterOn(builder, Boolean.TRUE); - } - }, UNSUPPRESSED { - @Override - protected void setQueryParameterOn(UriBuilder builder) { - ResponseCodes.setQueryParameterOn(builder, Boolean.FALSE); - } - }; - - /** - * Sets the suppress_response_codes query parameter by removing all existing ones - * and then appending it on the given UriBuilder. - */ - protected abstract void setQueryParameterOn(UriBuilder builder); - - private static void setQueryParameterOn(UriBuilder builder, Boolean value) { - // The Live SDK is designed to use our value of suppress_response_codes. - // If it uses any other value it could cause issues. Remove any previously - // existing suppress_response_codes and use ours. - builder.removeQueryParametersWithKey(QueryParameters.SUPPRESS_RESPONSE_CODES); - builder.appendQueryParameter(QueryParameters.SUPPRESS_RESPONSE_CODES, value.toString()); - } - } - - private static final Header LIVE_LIBRARY_HEADER = - new BasicHeader("X-HTTP-Live-Library", "android/" + Build.VERSION.RELEASE + "_" + - Config.INSTANCE.getApiVersion()); - private static final int SESSION_REFRESH_BUFFER_SECS = 30; - private static final int SESSION_TOKEN_SEND_BUFFER_SECS = 3; - - /** - * Constructs a new instance of a Header that contains the - * @param accessToken to construct inside the Authorization header - * @return a new instance of a Header that contains the Authorization access_token - */ - private static Header createAuthroizationHeader(LiveConnectSession session) { - assert session != null; - - String accessToken = session.getAccessToken(); - assert !TextUtils.isEmpty(accessToken); - - String tokenType = OAuth.TokenType.BEARER.toString().toLowerCase(); - String value = TextUtils.join(" ", new String[]{tokenType, accessToken}); - return new BasicHeader(AUTH.WWW_AUTH_RESP, value); - } - - private final HttpClient client; - private final List observers; - private final String path; - private final ResponseHandler responseHandler; - private final LiveConnectSession session; - - protected final UriBuilder requestUri; - - /** The original path string parsed into a Uri object. */ - protected final Uri pathUri; - - public ApiRequest(LiveConnectSession session, - HttpClient client, - ResponseHandler responseHandler, - String path) { - this(session, client, responseHandler, path, ResponseCodes.SUPPRESS, Redirects.SUPPRESS); - } - - /** - * Constructs a new instance of an ApiRequest and initializes its member variables - * - * @param session that contains the access_token - * @param client to make Http Requests on - * @param responseHandler to handle the response - * @param path of the request. it can be relative or absolute. - */ - public ApiRequest(LiveConnectSession session, - HttpClient client, - ResponseHandler responseHandler, - String path, - ResponseCodes responseCodes, - Redirects redirects) { - assert session != null; - assert client != null; - assert responseHandler != null; - assert !TextUtils.isEmpty(path); - - this.session = session; - this.client = client; - this.observers = new ArrayList(); - this.responseHandler = responseHandler; - this.path = path; - - UriBuilder builder; - this.pathUri = Uri.parse(path); - - if (this.pathUri.isAbsolute()) { - // if the path is absolute we will just use that entire path - builder = UriBuilder.newInstance(this.pathUri); - } else { - // if it is a relative path then we should use the config's API URI, - // which is usually something like https://apis.live.net/v5.0 - builder = UriBuilder.newInstance(Config.INSTANCE.getApiUri()) - .appendToPath(this.pathUri.getEncodedPath()) - .query(this.pathUri.getQuery()); - } - - responseCodes.setQueryParameterOn(builder); - redirects.setQueryParameterOn(builder); - - this.requestUri = builder; - } - - public void addObserver(Observer observer) { - this.observers.add(observer); - } - - /** - * Performs the Http Request and returns the response from the server - * - * @return an instance of ResponseType from the server - * @throws LiveOperationException if there was an error executing the HttpRequest - */ - public ResponseType execute() throws LiveOperationException { - // Let subclass decide which type of request to instantiate - HttpUriRequest request = this.createHttpRequest(); - - request.addHeader(LIVE_LIBRARY_HEADER); - - if (this.session.willExpireInSecs(SESSION_REFRESH_BUFFER_SECS)) { - this.session.refresh(); - } - - // if the session will soon expire, try to send the request without a token. - // the request *may* not need the token, let's give it a try rather than - // risk a request with an invalid token. - if (!this.session.willExpireInSecs(SESSION_TOKEN_SEND_BUFFER_SECS)) { - request.addHeader(createAuthroizationHeader(this.session)); - } - - try { - HttpResponse response = this.client.execute(request); - - for (Observer observer : this.observers) { - observer.onComplete(response); - } - - return this.responseHandler.handleResponse(response); - } catch (ClientProtocolException e) { - throw new LiveOperationException(ErrorMessages.SERVER_ERROR, e); - } catch (IOException e) { - // The IOException could contain a JSON object body - // (see InputStreamResponseHandler.java). If it does, - // we want to throw an exception with its message. If it does not, we want to wrap - // the IOException. - try { - new JSONObject(e.getMessage()); - throw new LiveOperationException(e.getMessage()); - } catch (JSONException jsonException) { - throw new LiveOperationException(ErrorMessages.SERVER_ERROR, e); - } - } - } - - /** @return the HTTP method being performed by the request */ - public abstract String getMethod(); - - /** @return the path of the request */ - public String getPath() { - return this.path; - } - - public void removeObserver(Observer observer) { - this.observers.remove(observer); - } - - /** - * Factory method that allows subclasses to choose which type of request will - * be performed. - * - * @return the HttpRequest to perform - * @throws LiveOperationException if there is an error creating the HttpRequest - */ - protected abstract HttpUriRequest createHttpRequest() throws LiveOperationException; -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ApiRequestAsync.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ApiRequestAsync.java deleted file mode 100644 index a5b1c35e..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ApiRequestAsync.java +++ /dev/null @@ -1,175 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.util.ArrayList; - -import android.os.AsyncTask; - -import com.microsoft.live.EntityEnclosingApiRequest.UploadProgressListener; - -/** - * ApiRequestAsync performs an async ApiRequest by subclassing AsyncTask - * and executing the request inside of doInBackground and giving the - * response to the appropriate listener on the main/UI thread. - */ -class ApiRequestAsync extends AsyncTask - implements UploadProgressListener { - - public interface Observer { - public void onComplete(ResponseType result); - - public void onError(LiveOperationException e); - } - - public interface ProgressObserver { - public void onProgress(Long... values); - } - - private class OnCompleteRunnable implements Runnable { - - private final ResponseType response; - - public OnCompleteRunnable(ResponseType response) { - assert response != null; - - this.response = response; - } - - @Override - public void run() { - for (Observer observer : observers) { - observer.onComplete(this.response); - } - } - } - - private class OnErrorRunnable implements Runnable { - - private final LiveOperationException exception; - - public OnErrorRunnable(LiveOperationException exception) { - assert exception != null; - - this.exception = exception; - } - - @Override - public void run() { - for (Observer observer : observers) { - observer.onError(this.exception); - } - } - } - - /** - * Static constructor. Prefer to use this over the normal constructor, because - * this will infer the generic types, and be less verbose. - * - * @param request - * @return a new ApiRequestAsync - */ - public static ApiRequestAsync newInstance(ApiRequest request) { - return new ApiRequestAsync(request); - } - - /** - * Static constructor. Prefer to use this over the normal constructor, because - * this will infer the generic types, and be less verbose. - * - * @param request - * @return a new ApiRequestAsync - */ - public static ApiRequestAsync newInstance(EntityEnclosingApiRequest request) { - return new ApiRequestAsync(request); - } - - private final ArrayList> observers; - private final ArrayList progressListeners; - private final ApiRequest request; - - { - this.observers = new ArrayList>(); - this.progressListeners = new ArrayList(); - } - - /** - * Constructs a new ApiRequestAsync object and initializes its member variables. - * - * This method attaches a progress observer to the EntityEnclosingApiRequest, and call - * publicProgress when ever there is an on progress event. - * - * @param request - */ - public ApiRequestAsync(EntityEnclosingApiRequest request) { - assert request != null; - - // Whenever the request has upload progress we need to publish the progress, so - // listen to progress events. - request.addListener(this); - - this.request = request; - } - - /** - * Constructs a new ApiRequestAsync object and initializes its member variables. - * - * @param operation to launch in an asynchronous manner - */ - public ApiRequestAsync(ApiRequest request) { - assert request != null; - - this.request = request; - } - - public boolean addObserver(Observer observer) { - return this.observers.add(observer); - } - - public boolean addProgressObserver(ProgressObserver observer) { - return this.progressListeners.add(observer); - } - - @Override - public void onProgress(long totalBytes, long numBytesWritten) { - publishProgress(Long.valueOf(totalBytes), Long.valueOf(numBytesWritten)); - } - - public boolean removeObserver(Observer observer) { - return this.observers.remove(observer); - } - - public boolean removeProgressObserver(ProgressObserver observer) { - return this.progressListeners.remove(observer); - } - - @Override - protected Runnable doInBackground(Void... args) { - ResponseType response; - - try { - response = this.request.execute(); - } catch (LiveOperationException e) { - return new OnErrorRunnable(e); - } - - return new OnCompleteRunnable(response); - } - - @Override - protected void onPostExecute(Runnable result) { - super.onPostExecute(result); - result.run(); - } - - @Override - protected void onProgressUpdate(Long... values) { - for (ProgressObserver listener : this.progressListeners) { - listener.onProgress(values); - } - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/AuthorizationRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/AuthorizationRequest.java deleted file mode 100644 index 757ea7c1..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/AuthorizationRequest.java +++ /dev/null @@ -1,508 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import org.apache.http.client.HttpClient; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.net.Uri; -import android.net.http.SslError; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.View; -import android.view.ViewGroup.LayoutParams; -import android.webkit.CookieManager; -import android.webkit.CookieSyncManager; -import android.webkit.SslErrorHandler; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.FrameLayout; -import android.widget.LinearLayout; - -/** - * AuthorizationRequest performs an Authorization Request by launching a WebView Dialog that - * displays the login and consent page and then, on a successful login and consent, performs an - * async AccessToken request. - */ -class AuthorizationRequest implements ObservableOAuthRequest, OAuthRequestObserver { - - /** - * OAuthDialog is a Dialog that contains a WebView. The WebView loads the passed in Uri, and - * loads the passed in WebViewClient that allows the WebView to be observed (i.e., when a page - * loads the WebViewClient will be notified). - */ - private class OAuthDialog extends Dialog implements OnCancelListener { - - /** - * AuthorizationWebViewClient is a static (i.e., does not have access to the instance that - * created it) class that checks for when the end_uri is loaded in to the WebView and calls - * the AuthorizationRequest's onEndUri method. - */ - private class AuthorizationWebViewClient extends WebViewClient { - - private final CookieManager cookieManager; - private final Set cookieKeys; - - public AuthorizationWebViewClient() { - // I believe I need to create a syncManager before I can use a cookie manager. - CookieSyncManager.createInstance(getContext()); - this.cookieManager = CookieManager.getInstance(); - this.cookieKeys = new HashSet(); - } - - /** - * Call back used when a page is being started. - * - * This will check to see if the given URL is one of the end_uris/redirect_uris and - * based on the query parameters the method will either return an error, or proceed with - * an AccessTokenRequest. - * - * @param view {@link WebView} that this is attached to. - * @param url of the page being started - */ - @Override - public void onPageFinished(WebView view, String url) { - Uri uri = Uri.parse(url); - - // only clear cookies that are on the logout domain. - if (uri.getHost().equals(Config.INSTANCE.getOAuthLogoutUri().getHost())) { - this.saveCookiesInMemory(this.cookieManager.getCookie(url)); - } - - Uri endUri = Config.INSTANCE.getOAuthDesktopUri(); - boolean isEndUri = UriComparator.INSTANCE.compare(uri, endUri) == 0; - if (!isEndUri) { - return; - } - - this.saveCookiesToPreferences(); - - AuthorizationRequest.this.onEndUri(uri); - OAuthDialog.this.dismiss(); - } - - /** - * Callback when the WebView received an Error. - * - * This method will notify the listener about the error and dismiss the WebView dialog. - * - * @param view the WebView that received the error - * @param errorCode the error code corresponding to a WebViewClient.ERROR_* value - * @param description the String containing the description of the error - * @param failingUrl the url that encountered an error - */ - @Override - public void onReceivedError(WebView view, - int errorCode, - String description, - String failingUrl) { - AuthorizationRequest.this.onError("", description, failingUrl); - OAuthDialog.this.dismiss(); - } -/* -Removed. Allows Man in the middle attacks and does not seem necessary. - @Override - public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - // TODO: Android does not like the SSL certificate we use, because it has '*' in - // it. Proceed with the errors. - handler.proceed(); - } -*/ - private void saveCookiesInMemory(String cookie) { - // Not all URLs will have cookies - if (TextUtils.isEmpty(cookie)) { - return; - } - - String[] pairs = TextUtils.split(cookie, "; "); - for (String pair : pairs) { - int index = pair.indexOf(EQUALS); - String key = pair.substring(0, index); - this.cookieKeys.add(key); - } - } - - private void saveCookiesToPreferences() { - SharedPreferences preferences = - getContext().getSharedPreferences(PreferencesConstants.FILE_NAME, - Context.MODE_PRIVATE); - - // If the application tries to login twice, before calling logout, there could - // be a cookie that was sent on the first login, that was not sent in the second - // login. So, read the cookies in that was saved before, and perform a union - // with the new cookies. - String value = preferences.getString(PreferencesConstants.COOKIES_KEY, ""); - String[] valueSplit = TextUtils.split(value, PreferencesConstants.COOKIE_DELIMITER); - - this.cookieKeys.addAll(Arrays.asList(valueSplit)); - - Editor editor = preferences.edit(); - value = TextUtils.join(PreferencesConstants.COOKIE_DELIMITER, this.cookieKeys); - editor.putString(PreferencesConstants.COOKIES_KEY, value); - editor.commit(); - - // we do not need to hold on to the cookieKeys in memory anymore. - // It could be garbage collected when this object does, but let's clear it now, - // since it will not be used again in the future. - this.cookieKeys.clear(); - } - } - - /** Uri to load */ - private final Uri requestUri; - - /** - * Constructs a new OAuthDialog. - * - * @param context to construct the Dialog in - * @param requestUri to load in the WebView - * @param webViewClient to be placed in the WebView - */ - public OAuthDialog(Uri requestUri) { - super(AuthorizationRequest.this.activity, android.R.style.Theme_Translucent_NoTitleBar); - this.setOwnerActivity(AuthorizationRequest.this.activity); - - assert requestUri != null; - this.requestUri = requestUri; - } - - /** Called when the user hits the back button on the dialog. */ - @Override - public void onCancel(DialogInterface dialog) { - LiveAuthException exception = new LiveAuthException(ErrorMessages.SIGNIN_CANCEL); - AuthorizationRequest.this.onException(exception); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - this.setOnCancelListener(this); - - FrameLayout content = new FrameLayout(this.getContext()); - LinearLayout webViewContainer = new LinearLayout(this.getContext()); - WebView webView = new WebView(this.getContext()); - - webView.setWebViewClient(new AuthorizationWebViewClient()); - - WebSettings webSettings = webView.getSettings(); - webSettings.setJavaScriptEnabled(true); - - webView.loadUrl(this.requestUri.toString()); - webView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT)); - webView.setVisibility(View.VISIBLE); - - webViewContainer.addView(webView); - webViewContainer.setVisibility(View.VISIBLE); - - content.addView(webViewContainer); - content.setVisibility(View.VISIBLE); - - content.forceLayout(); - webViewContainer.forceLayout(); - - this.addContentView(content, - new LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT)); - } - } - - /** - * Compares just the scheme, authority, and path. It does not compare the query parameters or - * the fragment. - */ - private enum UriComparator implements Comparator { - INSTANCE; - - @Override - public int compare(Uri lhs, Uri rhs) { - String[] lhsParts = { lhs.getScheme(), lhs.getAuthority(), lhs.getPath() }; - String[] rhsParts = { rhs.getScheme(), rhs.getAuthority(), rhs.getPath() }; - - assert lhsParts.length == rhsParts.length; - for (int i = 0; i < lhsParts.length; i++) { - int compare = lhsParts[i].compareTo(rhsParts[i]); - if (compare != 0) { - return compare; - } - } - - return 0; - } - } - - private static final String AMPERSAND = "&"; - private static final String EQUALS = "="; - - /** - * Turns the fragment parameters of the uri into a map. - * - * @param uri to get fragment parameters from - * @return a map containing the fragment parameters - */ - private static Map getFragmentParametersMap(Uri uri) { - String fragment = uri.getFragment(); - String[] keyValuePairs = TextUtils.split(fragment, AMPERSAND); - Map fragementParameters = new HashMap(); - - for (String keyValuePair : keyValuePairs) { - int index = keyValuePair.indexOf(EQUALS); - String key = keyValuePair.substring(0, index); - String value = keyValuePair.substring(index + 1); - fragementParameters.put(key, value); - } - - return fragementParameters; - } - - private final Activity activity; - private final HttpClient client; - private final String clientId; - private final DefaultObservableOAuthRequest observable; - private final String redirectUri; - private final String scope; - - public AuthorizationRequest(Activity activity, - HttpClient client, - String clientId, - String redirectUri, - String scope) { - assert activity != null; - assert client != null; - assert !TextUtils.isEmpty(clientId); - assert !TextUtils.isEmpty(redirectUri); - assert !TextUtils.isEmpty(scope); - - this.activity = activity; - this.client = client; - this.clientId = clientId; - this.redirectUri = redirectUri; - this.observable = new DefaultObservableOAuthRequest(); - this.scope = scope; - } - - @Override - public void addObserver(OAuthRequestObserver observer) { - this.observable.addObserver(observer); - } - - /** - * Launches the login/consent page inside of a Dialog that contains a WebView and then performs - * a AccessTokenRequest on successful login and consent. This method is async and will call the - * passed in listener when it is completed. - */ - public void execute() { - String displayType = this.getDisplayParameter(); - String responseType = OAuth.ResponseType.CODE.toString().toLowerCase(); - String locale = Locale.getDefault().toString(); - Uri requestUri = Config.INSTANCE.getOAuthAuthorizeUri() - .buildUpon() - .appendQueryParameter(OAuth.CLIENT_ID, this.clientId) - .appendQueryParameter(OAuth.SCOPE, this.scope) - .appendQueryParameter(OAuth.DISPLAY, displayType) - .appendQueryParameter(OAuth.RESPONSE_TYPE, responseType) - .appendQueryParameter(OAuth.LOCALE, locale) - .appendQueryParameter(OAuth.REDIRECT_URI, this.redirectUri) - .build(); - - OAuthDialog oAuthDialog = new OAuthDialog(requestUri); - oAuthDialog.show(); - } - - @Override - public void onException(LiveAuthException exception) { - this.observable.notifyObservers(exception); - } - - @Override - public void onResponse(OAuthResponse response) { - this.observable.notifyObservers(response); - } - - @Override - public boolean removeObserver(OAuthRequestObserver observer) { - return this.observable.removeObserver(observer); - } - - /** - * Gets the display parameter by looking at the screen size of the activity. - * @return "android_phone" for phones and "android_tablet" for tablets. - */ - private String getDisplayParameter() { - ScreenSize screenSize = ScreenSize.determineScreenSize(this.activity); - DeviceType deviceType = screenSize.getDeviceType(); - - return deviceType.getDisplayParameter().toString().toLowerCase(); - } - - /** - * Called when the response uri contains an access_token in the fragment. - * - * This method reads the response and calls back the LiveOAuthListener on the UI/main thread, - * and then dismisses the dialog window. - * - * See Section - * 1.3.1 of the OAuth 2.0 spec. - * - * @param fragmentParameters in the uri - */ - private void onAccessTokenResponse(Map fragmentParameters) { - assert fragmentParameters != null; - - OAuthSuccessfulResponse response; - try { - response = OAuthSuccessfulResponse.createFromFragment(fragmentParameters); - } catch (LiveAuthException e) { - this.onException(e); - return; - } - - this.onResponse(response); - } - - /** - * Called when the response uri contains an authorization code. - * - * This method launches an async AccessTokenRequest and dismisses the dialog window. - * - * See Section - * 4.1.2 of the OAuth 2.0 spec for more information. - * - * @param code is the authorization code from the uri - */ - private void onAuthorizationResponse(String code) { - assert !TextUtils.isEmpty(code); - - // Since we DO have an authorization code, launch an AccessTokenRequest. - // We do this asynchronously to prevent the HTTP IO from occupying the - // UI/main thread (which we are on right now). - AccessTokenRequest request = new AccessTokenRequest(this.client, - this.clientId, - this.redirectUri, - code); - - TokenRequestAsync requestAsync = new TokenRequestAsync(request); - // We want to know when this request finishes, because we need to notify our - // observers. - requestAsync.addObserver(this); - requestAsync.execute(); - } - - /** - * Called when the end uri is loaded. - * - * This method will read the uri's query parameters and fragment, and respond with the - * appropriate action. - * - * @param endUri that was loaded - */ - private void onEndUri(Uri endUri) { - // If we are on an end uri, the response could either be in - // the fragment or the query parameters. The response could - // either be successful or it could contain an error. - // Check all situations and call the listener's appropriate callback. - // Callback the listener on the UI/main thread. We could call it right away since - // we are on the UI/main thread, but it is probably better that we finish up with - // the WebView code before we callback on the listener. - boolean hasFragment = endUri.getFragment() != null; - boolean hasQueryParameters = endUri.getQuery() != null; - boolean invalidUri = !hasFragment && !hasQueryParameters; - - // check for an invalid uri, and leave early - if (invalidUri) { - this.onInvalidUri(); - return; - } - - if (hasFragment) { - Map fragmentParameters = - AuthorizationRequest.getFragmentParametersMap(endUri); - - boolean isSuccessfulResponse = - fragmentParameters.containsKey(OAuth.ACCESS_TOKEN) && - fragmentParameters.containsKey(OAuth.TOKEN_TYPE); - if (isSuccessfulResponse) { - this.onAccessTokenResponse(fragmentParameters); - return; - } - - String error = fragmentParameters.get(OAuth.ERROR); - if (error != null) { - String errorDescription = fragmentParameters.get(OAuth.ERROR_DESCRIPTION); - String errorUri = fragmentParameters.get(OAuth.ERROR_URI); - this.onError(error, errorDescription, errorUri); - return; - } - } - - if (hasQueryParameters) { - String code = endUri.getQueryParameter(OAuth.CODE); - if (code != null) { - this.onAuthorizationResponse(code); - return; - } - - String error = endUri.getQueryParameter(OAuth.ERROR); - if (error != null) { - String errorDescription = endUri.getQueryParameter(OAuth.ERROR_DESCRIPTION); - String errorUri = endUri.getQueryParameter(OAuth.ERROR_URI); - this.onError(error, errorDescription, errorUri); - return; - } - } - - // if the code reaches this point, the uri was invalid - // because it did not contain either a successful response - // or an error in either the queryParameter or the fragment - this.onInvalidUri(); - } - - /** - * Called when end uri had an error in either the fragment or the query parameter. - * - * This method constructs the proper exception, calls the listener's appropriate callback method - * on the main/UI thread, and then dismisses the dialog window. - * - * @param error containing an error code - * @param errorDescription optional text with additional information - * @param errorUri optional uri that is associated with the error. - */ - private void onError(String error, String errorDescription, String errorUri) { - LiveAuthException exception = new LiveAuthException(error, - errorDescription, - errorUri); - this.onException(exception); - } - - /** - * Called when an invalid uri (i.e., a uri that does not contain an error or a successful - * response). - * - * This method constructs an exception, calls the listener's appropriate callback on the main/UI - * thread, and then dismisses the dialog window. - */ - private void onInvalidUri() { - LiveAuthException exception = new LiveAuthException(ErrorMessages.SERVER_ERROR); - this.onException(exception); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/Config.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/Config.java deleted file mode 100644 index 7b9536cc..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/Config.java +++ /dev/null @@ -1,89 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import android.net.Uri; -import android.text.TextUtils; - -/** - * Config is a singleton class that contains the values used throughout the SDK. - */ -enum Config { - INSTANCE; - - private Uri apiUri; - private String apiVersion; - private Uri oAuthAuthorizeUri; - private Uri oAuthDesktopUri; - private Uri oAuthLogoutUri; - private Uri oAuthTokenUri; - - Config() { - // initialize default values for constants - apiUri = Uri.parse("https://apis.live.net/v5.0"); - apiVersion = "5.0"; - oAuthAuthorizeUri = Uri.parse("https://login.live.com/oauth20_authorize.srf"); - oAuthDesktopUri = Uri.parse("https://login.live.com/oauth20_desktop.srf"); - oAuthLogoutUri = Uri.parse("https://login.live.com/oauth20_logout.srf"); - oAuthTokenUri = Uri.parse("https://login.live.com/oauth20_token.srf"); - } - - public Uri getApiUri() { - return apiUri; - } - - public String getApiVersion() { - return apiVersion; - } - - public Uri getOAuthAuthorizeUri() { - return oAuthAuthorizeUri; - } - - public Uri getOAuthDesktopUri() { - return oAuthDesktopUri; - } - - public Uri getOAuthLogoutUri() { - return oAuthLogoutUri; - } - - public Uri getOAuthTokenUri() { - return oAuthTokenUri; - } - - public void setApiUri(Uri apiUri) { - assert apiUri != null; - this.apiUri = apiUri; - } - - public void setApiVersion(String apiVersion) { - assert !TextUtils.isEmpty(apiVersion); - this.apiVersion = apiVersion; - } - - public void setOAuthAuthorizeUri(Uri oAuthAuthorizeUri) { - assert oAuthAuthorizeUri != null; - this.oAuthAuthorizeUri = oAuthAuthorizeUri; - } - - public void setOAuthDesktopUri(Uri oAuthDesktopUri) { - assert oAuthDesktopUri != null; - this.oAuthDesktopUri = oAuthDesktopUri; - } - - public void setOAuthLogoutUri(Uri oAuthLogoutUri) { - assert oAuthLogoutUri != null; - - this.oAuthLogoutUri = oAuthLogoutUri; - } - - public void setOAuthTokenUri(Uri oAuthTokenUri) { - assert oAuthTokenUri != null; - this.oAuthTokenUri = oAuthTokenUri; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/CopyRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/CopyRequest.java deleted file mode 100644 index 7597cff4..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/CopyRequest.java +++ /dev/null @@ -1,55 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import org.apache.http.HttpEntity; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpUriRequest; -import org.json.JSONObject; - -/** - * CopyRequest is a subclass of a BodyEnclosingApiRequest and performs a Copy request. - */ -class CopyRequest extends EntityEnclosingApiRequest { - - public static final String METHOD = HttpCopy.METHOD_NAME; - - /** - * Constructs a new CopyRequest and initializes its member variables. - * - * @param session with the access_token - * @param client to make Http requests on - * @param path of the request - * @param entity body of the request - */ - public CopyRequest(LiveConnectSession session, - HttpClient client, - String path, - HttpEntity entity) { - super(session, client, JsonResponseHandler.INSTANCE, path, entity); - } - - /** @return the string "COPY" */ - @Override - public String getMethod() { - return METHOD; - } - - /** - * Factory method override that constructs a HttpCopy and adds a body to it. - * - * @return a HttpCopy with the properly body added to it. - */ - @Override - protected HttpUriRequest createHttpRequest() throws LiveOperationException { - final HttpCopy request = new HttpCopy(this.requestUri.toString()); - - request.setEntity(this.entity); - - return request; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DefaultObservableOAuthRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DefaultObservableOAuthRequest.java deleted file mode 100644 index e302d64d..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DefaultObservableOAuthRequest.java +++ /dev/null @@ -1,55 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.util.ArrayList; -import java.util.List; - -/** - * Default implementation of an ObserverableOAuthRequest. - * Other classes that need to be observed can compose themselves out of this class. - */ -class DefaultObservableOAuthRequest implements ObservableOAuthRequest { - - private final List observers; - - public DefaultObservableOAuthRequest() { - this.observers = new ArrayList(); - } - - @Override - public void addObserver(OAuthRequestObserver observer) { - this.observers.add(observer); - } - - /** - * Calls all the Observerable's observer's onException. - * - * @param exception to give to the observers - */ - public void notifyObservers(LiveAuthException exception) { - for (final OAuthRequestObserver observer : this.observers) { - observer.onException(exception); - } - } - - /** - * Calls all this Observable's observer's onResponse. - * - * @param response to give to the observers - */ - public void notifyObservers(OAuthResponse response) { - for (final OAuthRequestObserver observer : this.observers) { - observer.onResponse(response); - } - } - - @Override - public boolean removeObserver(OAuthRequestObserver observer) { - return this.observers.remove(observer); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DeleteRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DeleteRequest.java deleted file mode 100644 index 7dd7ab54..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DeleteRequest.java +++ /dev/null @@ -1,47 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpUriRequest; -import org.json.JSONObject; - -/** - * DeleteRequest is a subclass of an ApiRequest and performs a delete request. - */ -class DeleteRequest extends ApiRequest { - - public static final String METHOD = HttpDelete.METHOD_NAME; - - /** - * Constructs a new DeleteRequest and initializes its member variables. - * - * @param session with the access_token - * @param client to perform Http requests on - * @param path of the request - */ - public DeleteRequest(LiveConnectSession session, HttpClient client, String path) { - super(session, client, JsonResponseHandler.INSTANCE, path); - } - - /** @return the string "DELETE" */ - @Override - public String getMethod() { - return METHOD; - } - - /** - * Factory method override that constructs a HttpDelete request - * - * @return a HttpDelete request - */ - @Override - protected HttpUriRequest createHttpRequest() { - return new HttpDelete(this.requestUri.toString()); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DeviceType.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DeviceType.java deleted file mode 100644 index a4e50e06..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DeviceType.java +++ /dev/null @@ -1,31 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import com.microsoft.live.OAuth.DisplayType; - -/** - * The type of the device is used to determine the display query parameter for login.live.com. - * Phones have a display parameter of android_phone. - * Tablets have a display parameter of android_tablet. - */ -enum DeviceType { - PHONE { - @Override - public DisplayType getDisplayParameter() { - return DisplayType.ANDROID_PHONE; - } - }, - TABLET { - @Override - public DisplayType getDisplayParameter() { - return DisplayType.ANDROID_TABLET; - } - }; - - abstract public DisplayType getDisplayParameter(); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DownloadRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DownloadRequest.java deleted file mode 100644 index 4d05c741..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/DownloadRequest.java +++ /dev/null @@ -1,37 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.io.InputStream; - -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; - -class DownloadRequest extends ApiRequest { - - public static final String METHOD = HttpGet.METHOD_NAME; - - public DownloadRequest(LiveConnectSession session, HttpClient client, String path) { - super(session, - client, - InputStreamResponseHandler.INSTANCE, - path, - ResponseCodes.UNSUPPRESSED, - Redirects.UNSUPPRESSED); - } - - @Override - public String getMethod() { - return METHOD; - } - - @Override - protected HttpUriRequest createHttpRequest() throws LiveOperationException { - return new HttpGet(this.requestUri.toString()); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/EntityEnclosingApiRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/EntityEnclosingApiRequest.java deleted file mode 100644 index 61fb4147..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/EntityEnclosingApiRequest.java +++ /dev/null @@ -1,184 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; - -import org.apache.http.HttpEntity; -import org.apache.http.client.HttpClient; -import org.apache.http.client.ResponseHandler; -import org.apache.http.entity.HttpEntityWrapper; - -/** - * EntityEnclosingApiRequest is an ApiRequest with a body. - * Upload progress can be monitored by adding an UploadProgressListener to this class. - */ -abstract class EntityEnclosingApiRequest extends ApiRequest { - - /** - * UploadProgressListener is a listener that is called during upload progress. - */ - public interface UploadProgressListener { - - /** - * @param totalBytes of the upload request - * @param numBytesWritten during the upload request - */ - public void onProgress(long totalBytes, long numBytesWritten); - } - - /** - * Wraps the given entity, and intercepts writeTo calls to check the upload progress. - */ - private static class ProgressableEntity extends HttpEntityWrapper { - - final List listeners; - - ProgressableEntity(HttpEntity wrapped, List listeners) { - super(wrapped); - - assert listeners != null; - this.listeners = listeners; - } - - @Override - public void writeTo(OutputStream outstream) throws IOException { - this.wrappedEntity.writeTo(new ProgressableOutputStream(outstream, - this.getContentLength(), - this.listeners)); - // If we don't consume the content, the content will be leaked (i.e., the InputStream - // in the HttpEntity is not closed). - // You'd think the library would call this. - this.wrappedEntity.consumeContent(); - } - } - - /** - * Wraps the given output stream and notifies the given listeners, when the - * stream is written to. - */ - private static class ProgressableOutputStream extends FilterOutputStream { - - final List listeners; - long numBytesWritten; - long totalBytes; - - public ProgressableOutputStream(OutputStream outstream, - long totalBytes, - List listeners) { - super(outstream); - - assert totalBytes >= 0L; - assert listeners != null; - - this.listeners = listeners; - this.numBytesWritten = 0L; - this.totalBytes = totalBytes; - } - - @Override - public void write(byte[] buffer) throws IOException { - this.out.write(buffer); - - this.numBytesWritten += buffer.length; - this.notifyListeners(); - } - - @Override - public void write(byte[] buffer, int offset, int count) throws IOException { - this.out.write(buffer, offset, count); - - this.numBytesWritten += count; - this.notifyListeners(); - } - - @Override - public void write(int oneByte) throws IOException { - this.out.write(oneByte); - - this.numBytesWritten += 1; - this.notifyListeners(); - } - - private void notifyListeners() { - assert this.numBytesWritten <= this.totalBytes; - - for (final UploadProgressListener listener : this.listeners) { - listener.onProgress(this.totalBytes, this.numBytesWritten); - } - } - } - - protected final HttpEntity entity; - - private final List listeners; - - public EntityEnclosingApiRequest(LiveConnectSession session, - HttpClient client, - ResponseHandler responseHandler, - String path, - HttpEntity entity) { - this(session, - client, - responseHandler, - path, - entity, - ResponseCodes.SUPPRESS, - Redirects.SUPPRESS); - } - - /** - * Constructs a new EntiyEnclosingApiRequest and initializes its member variables. - * - * @param session that contains the access token - * @param client to make Http Requests on - * @param path of the request - * @param entity of the request - */ - public EntityEnclosingApiRequest(LiveConnectSession session, - HttpClient client, - ResponseHandler responseHandler, - String path, - HttpEntity entity, - ResponseCodes responseCodes, - Redirects redirects) { - super(session, client, responseHandler, path, responseCodes, redirects); - - assert entity != null; - - this.listeners = new ArrayList(); - this.entity = new ProgressableEntity(entity, this.listeners); - } - - /** - * Adds an UploadProgressListener to be called when there is upload progress. - * - * @param listener to add - * @return always true - */ - public boolean addListener(UploadProgressListener listener) { - assert listener != null; - - return this.listeners.add(listener); - } - - /** - * Removes an UploadProgressListener. - * - * @param listener to be removed - * @return true if the the listener was removed - */ - public boolean removeListener(UploadProgressListener listener) { - assert listener != null; - - return this.listeners.remove(listener); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ErrorMessages.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ErrorMessages.java deleted file mode 100644 index 29b163bb..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ErrorMessages.java +++ /dev/null @@ -1,36 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * ErrorMessages is a non-instantiable class that contains all the String constants - * used in for errors and exceptions. - */ -final class ErrorMessages { - public static final String ABSOLUTE_PARAMETER = - "Input parameter '%1$s' is invalid. '%1$s' cannot be absolute."; - public static final String CLIENT_ERROR = - "An error occured on the client during the operation."; - public static final String EMPTY_PARAMETER = - "Input parameter '%1$s' is invalid. '%1$s' cannot be empty."; - public static final String INVALID_URI = - "Input parameter '%1$s' is invalid. '%1$s' must be a valid URI."; - public static final String LOGGED_OUT = "The user has is logged out."; - public static final String LOGIN_IN_PROGRESS = - "Another login operation is already in progress."; - public static final String MISSING_UPLOAD_LOCATION = - "The provided path does not contain an upload_location."; - public static final String NON_INSTANTIABLE_CLASS = "Non-instantiable class"; - public static final String NULL_PARAMETER = - "Input parameter '%1$s' is invalid. '%1$s' cannot be null."; - public static final String SERVER_ERROR = - "An error occured while communicating with the server during the operation. " + - "Please try again later."; - public static final String SIGNIN_CANCEL = "The user cancelled the login operation."; - - private ErrorMessages() { throw new AssertionError(NON_INSTANTIABLE_CLASS); } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/GetRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/GetRequest.java deleted file mode 100644 index cd34857f..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/GetRequest.java +++ /dev/null @@ -1,47 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.json.JSONObject; - -/** - * GetRequest is a subclass of an ApiRequest and performs a GET request. - */ -class GetRequest extends ApiRequest { - - public static final String METHOD = HttpGet.METHOD_NAME; - - /** - * Constructs a new GetRequest and initializes its member variables. - * - * @param session with the access_token - * @param client to perform Http requests on - * @param path of the request - */ - public GetRequest(LiveConnectSession session, HttpClient client, String path) { - super(session, client, JsonResponseHandler.INSTANCE, path); - } - - /** @return the string "GET" */ - @Override - public String getMethod() { - return METHOD; - } - - /** - * Factory method override that constructs a HttpGet request - * - * @return a HttpGet request - */ - @Override - protected HttpUriRequest createHttpRequest() { - return new HttpGet(this.requestUri.toString()); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/HttpCopy.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/HttpCopy.java deleted file mode 100644 index 9df83ad7..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/HttpCopy.java +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.net.URI; -import java.net.URISyntaxException; - -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; - -/** - * HttpCopy represents an HTTP COPY operation. - * HTTP COPY is not a standard HTTP method and this adds it - * to the HTTP library. - */ -class HttpCopy extends HttpEntityEnclosingRequestBase { - - public static final String METHOD_NAME = "COPY"; - - /** - * Constructs a new HttpCopy with the given uri and initializes its member variables. - * - * @param uri of the request - */ - public HttpCopy(String uri) { - try { - this.setURI(new URI(uri)); - } catch (URISyntaxException e) { - final String message = String.format(ErrorMessages.INVALID_URI, "uri"); - throw new IllegalArgumentException(message); - } - } - - /** @return the string "COPY" */ - @Override - public String getMethod() { - return METHOD_NAME; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/HttpMove.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/HttpMove.java deleted file mode 100644 index 89749f74..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/HttpMove.java +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.net.URI; -import java.net.URISyntaxException; - -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; - -/** - * HttpMove represents an HTTP MOVE operation. - * HTTP MOVE is not a standard HTTP method and this adds it - * to the HTTP library. - */ -class HttpMove extends HttpEntityEnclosingRequestBase { - - public static final String METHOD_NAME = "MOVE"; - - /** - * Constructs a new HttpMove with the given uri and initializes its member variables. - * - * @param uri of the request - */ - public HttpMove(String uri) { - try { - this.setURI(new URI(uri)); - } catch (URISyntaxException e) { - final String message = String.format(ErrorMessages.INVALID_URI, "uri"); - throw new IllegalArgumentException(message); - } - } - - /** @return the string "MOVE" */ - @Override - public String getMethod() { - return METHOD_NAME; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/InputStreamResponseHandler.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/InputStreamResponseHandler.java deleted file mode 100644 index 3965490b..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/InputStreamResponseHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.io.IOException; -import java.io.InputStream; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.util.EntityUtils; - -/** - * InputStreamResponseHandler returns an InputStream from an HttpResponse. - * Singleton--use INSTANCE. - */ -enum InputStreamResponseHandler implements ResponseHandler { - INSTANCE; - - @Override - public InputStream handleResponse(HttpResponse response) throws ClientProtocolException, - IOException { - HttpEntity entity = response.getEntity(); - StatusLine statusLine = response.getStatusLine(); - boolean successfulResponse = (statusLine.getStatusCode() / 100) == 2; - if (!successfulResponse) { - // If it was not a successful response, the response body contains a - // JSON error message body. Unfortunately, I have to adhere to the interface - // and I am throwing an IOException in this case. - String responseBody = EntityUtils.toString(entity); - throw new IOException(responseBody); - } - - return entity.getContent(); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/JsonEntity.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/JsonEntity.java deleted file mode 100644 index f52ff0d0..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/JsonEntity.java +++ /dev/null @@ -1,33 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.io.UnsupportedEncodingException; - -import org.apache.http.entity.StringEntity; -import org.apache.http.protocol.HTTP; -import org.json.JSONObject; - -/** - * JsonEntity is an Entity that contains a Json body - */ -class JsonEntity extends StringEntity { - - public static final String CONTENT_TYPE = "application/json;charset=" + HTTP.UTF_8; - - /** - * Constructs a new JsonEntity. - * - * @param body - * @throws UnsupportedEncodingException - */ - JsonEntity(JSONObject body) throws UnsupportedEncodingException { - super(body.toString(), HTTP.UTF_8); - - this.setContentType(CONTENT_TYPE); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/JsonResponseHandler.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/JsonResponseHandler.java deleted file mode 100644 index 98c1a31a..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/JsonResponseHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.io.IOException; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.util.EntityUtils; -import org.json.JSONException; -import org.json.JSONObject; - -import android.text.TextUtils; - -/** - * JsonResponseHandler returns a JSONObject from an HttpResponse. - * Singleton--use INSTANCE. - */ -enum JsonResponseHandler implements ResponseHandler { - INSTANCE; - - @Override - public JSONObject handleResponse(HttpResponse response) - throws ClientProtocolException, IOException { - final HttpEntity entity = response.getEntity(); - final String stringResponse; - if (entity != null) { - stringResponse = EntityUtils.toString(entity); - } else { - return null; - } - - if (TextUtils.isEmpty(stringResponse)) { - return new JSONObject(); - } - - try { - return new JSONObject(stringResponse); - } catch (JSONException e) { - throw new IOException(e.getLocalizedMessage()); - } - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveAuthClient.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveAuthClient.java deleted file mode 100644 index e917ae76..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveAuthClient.java +++ /dev/null @@ -1,640 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.apache.http.client.HttpClient; -import org.apache.http.impl.client.DefaultHttpClient; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.net.Uri; -import android.os.AsyncTask; -import android.text.TextUtils; -import android.util.Log; -import android.webkit.CookieManager; -import android.webkit.CookieSyncManager; - -import com.microsoft.live.OAuth.ErrorType; - -/** - * {@code LiveAuthClient} is a class responsible for retrieving a {@link LiveConnectSession}, which - * can be given to a {@link LiveConnectClient} in order to make requests to the Live Connect API. - */ -public class LiveAuthClient { - - private static class AuthCompleteRunnable extends AuthListenerCaller implements Runnable { - - private final LiveStatus status; - private final LiveConnectSession session; - - public AuthCompleteRunnable(LiveAuthListener listener, - Object userState, - LiveStatus status, - LiveConnectSession session) { - super(listener, userState); - this.status = status; - this.session = session; - } - - @Override - public void run() { - listener.onAuthComplete(status, session, userState); - } - } - - private static class AuthErrorRunnable extends AuthListenerCaller implements Runnable { - - private final LiveAuthException exception; - - public AuthErrorRunnable(LiveAuthListener listener, - Object userState, - LiveAuthException exception) { - super(listener, userState); - this.exception = exception; - } - - @Override - public void run() { - listener.onAuthError(exception, userState); - } - - } - - private static abstract class AuthListenerCaller { - protected final LiveAuthListener listener; - protected final Object userState; - - public AuthListenerCaller(LiveAuthListener listener, Object userState) { - this.listener = listener; - this.userState = userState; - } - } - - /** - * This class observes an {@link OAuthRequest} and calls the appropriate Listener method. - * On a successful response, it will call the - * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)}. - * On an exception or an unsuccessful response, it will call - * {@link LiveAuthListener#onAuthError(LiveAuthException, Object)}. - */ - private class ListenerCallerObserver extends AuthListenerCaller - implements OAuthRequestObserver, - OAuthResponseVisitor { - - public ListenerCallerObserver(LiveAuthListener listener, Object userState) { - super(listener, userState); - } - - @Override - public void onException(LiveAuthException exception) { - new AuthErrorRunnable(listener, userState, exception).run(); - } - - @Override - public void onResponse(OAuthResponse response) { - response.accept(this); - } - - @Override - public void visit(OAuthErrorResponse response) { - String error = response.getError().toString().toLowerCase(); - String errorDescription = response.getErrorDescription(); - String errorUri = response.getErrorUri(); - LiveAuthException exception = new LiveAuthException(error, - errorDescription, - errorUri); - - new AuthErrorRunnable(listener, userState, exception).run(); - } - - @Override - public void visit(OAuthSuccessfulResponse response) { - session.loadFromOAuthResponse(response); - - new AuthCompleteRunnable(listener, userState, LiveStatus.CONNECTED, session).run(); - } - } - - /** Observer that will, depending on the response, save or clear the refresh token. */ - private class RefreshTokenWriter implements OAuthRequestObserver, OAuthResponseVisitor { - - @Override - public void onException(LiveAuthException exception) { } - - @Override - public void onResponse(OAuthResponse response) { - response.accept(this); - } - - @Override - public void visit(OAuthErrorResponse response) { - if (response.getError() == ErrorType.INVALID_GRANT) { - LiveAuthClient.this.clearRefreshTokenFromPreferences(); - } - } - - @Override - public void visit(OAuthSuccessfulResponse response) { - String refreshToken = response.getRefreshToken(); - if (!TextUtils.isEmpty(refreshToken)) { - this.saveRefreshTokenToPerferences(refreshToken); - } - } - - private boolean saveRefreshTokenToPerferences(String refreshToken) { - assert !TextUtils.isEmpty(refreshToken); - Log.w("MYLIVE", "saveRefreshTokenToPerferences"); - - SharedPreferences settings = - applicationContext.getSharedPreferences(PreferencesConstants.FILE_NAME, - Context.MODE_PRIVATE); - Editor editor = settings.edit(); - editor.putString(PreferencesConstants.REFRESH_TOKEN_KEY, refreshToken); - - - boolean res = editor.commit(); - Log.w("MYLIVE", "saveRefreshTokenToPerferences done for token "+refreshToken+" res="+res); - - return res; - } - } - - /** - * An {@link OAuthResponseVisitor} that checks the {@link OAuthResponse} and if it is a - * successful response, it loads the response into the given session. - */ - private static class SessionRefresher implements OAuthResponseVisitor { - - private final LiveConnectSession session; - private boolean visitedSuccessfulResponse; - - public SessionRefresher(LiveConnectSession session) { - assert session != null; - - this.session = session; - this.visitedSuccessfulResponse = false; - } - - @Override - public void visit(OAuthErrorResponse response) { - this.visitedSuccessfulResponse = false; - } - - @Override - public void visit(OAuthSuccessfulResponse response) { - this.session.loadFromOAuthResponse(response); - this.visitedSuccessfulResponse = true; - } - - public boolean visitedSuccessfulResponse() { - return this.visitedSuccessfulResponse; - } - } - - /** - * A LiveAuthListener that does nothing on each of the call backs. - * This is used so when a null listener is passed in, this can be used, instead of null, - * to avoid if (listener == null) checks. - */ - private static final LiveAuthListener NULL_LISTENER = new LiveAuthListener() { - @Override - public void onAuthComplete(LiveStatus status, LiveConnectSession session, Object sender) { } - @Override - public void onAuthError(LiveAuthException exception, Object sender) { } - }; - - private final Context applicationContext; - private final String clientId; - private boolean hasPendingLoginRequest; - - /** - * Responsible for all network (i.e., HTTP) calls. - * Tests will want to change this to mock the network and HTTP responses. - * @see #setHttpClient(HttpClient) - */ - private HttpClient httpClient; - - /** saved from initialize and used in the login call if login's scopes are null. */ - private Set scopesFromInitialize; - - /** One-to-one relationship between LiveAuthClient and LiveConnectSession. */ - private final LiveConnectSession session; - - { - this.httpClient = new DefaultHttpClient(); - this.hasPendingLoginRequest = false; - this.session = new LiveConnectSession(this); - } - - /** - * Constructs a new {@code LiveAuthClient} instance and initializes its member variables. - * - * @param context Context of the Application used to save any refresh_token. - * @param clientId The client_id of the Live Connect Application to login to. - */ - public LiveAuthClient(Context context, String clientId) { - LiveConnectUtils.assertNotNull(context, "context"); - LiveConnectUtils.assertNotNullOrEmpty(clientId, "clientId"); - - this.applicationContext = context.getApplicationContext(); - this.clientId = clientId; - } - - /** @return the client_id of the Live Connect application. */ - public String getClientId() { - return this.clientId; - } - - /** - * Initializes a new {@link LiveConnectSession} with the given scopes. - * - * The {@link LiveConnectSession} will be returned by calling - * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)}. - * Otherwise, the {@link LiveAuthListener#onAuthError(LiveAuthException, Object)} will be - * called. These methods will be called on the main/UI thread. - * - * If the wl.offline_access scope is used, a refresh_token is stored in the given - * {@link Activity}'s {@link SharedPerfences}. - * - * @param scopes to initialize the {@link LiveConnectSession} with. - * See MSDN Live Connect - * Reference's Scopes and permissions for a list of scopes and explanations. - * @param listener called on either completion or error during the initialize process. - */ - public void initialize(Iterable scopes, LiveAuthListener listener) { - this.initialize(scopes, listener, null); - } - - /** - * Initializes a new {@link LiveConnectSession} with the given scopes. - * - * The {@link LiveConnectSession} will be returned by calling - * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)}. - * Otherwise, the {@link LiveAuthListener#onAuthError(LiveAuthException, Object)} will be - * called. These methods will be called on the main/UI thread. - * - * If the wl.offline_access scope is used, a refresh_token is stored in the given - * {@link Activity}'s {@link SharedPerfences}. - * - * @param scopes to initialize the {@link LiveConnectSession} with. - * See MSDN Live Connect - * Reference's Scopes and permissions for a list of scopes and explanations. - * @param listener called on either completion or error during the initialize process - * @param userState arbitrary object that is used to determine the caller of the method. - */ - public void initialize(Iterable scopes, LiveAuthListener listener, Object userState) { - TokenRequestAsync asyncRequest = getInitializeRequest(scopes, listener, - userState); - if (asyncRequest == null) - { - return; - } - - asyncRequest.execute(); - } - - public void initializeSynchronous(Iterable scopes, LiveAuthListener listener, Object userState) { - TokenRequestAsync asyncRequest = getInitializeRequest(scopes, listener, - userState); - if (asyncRequest == null) - { - return; - } - - asyncRequest.executeSynchronous(); - } - - private TokenRequestAsync getInitializeRequest(Iterable scopes, - LiveAuthListener listener, Object userState) { - if (listener == null) { - listener = NULL_LISTENER; - } - - if (scopes == null) { - scopes = Arrays.asList(new String[0]); - } - - // copy scopes for login - this.scopesFromInitialize = new HashSet(); - for (String scope : scopes) { - this.scopesFromInitialize.add(scope); - } - this.scopesFromInitialize = Collections.unmodifiableSet(this.scopesFromInitialize); - - String refreshToken = this.getRefreshTokenFromPreferences(); - - if (refreshToken == null) { - listener.onAuthComplete(LiveStatus.UNKNOWN, null, userState); - return null; - } - - RefreshAccessTokenRequest request = - new RefreshAccessTokenRequest(this.httpClient, - this.clientId, - refreshToken, - TextUtils.join(OAuth.SCOPE_DELIMITER, scopes)); - TokenRequestAsync asyncRequest = new TokenRequestAsync(request); - - asyncRequest.addObserver(new ListenerCallerObserver(listener, userState)); - asyncRequest.addObserver(new RefreshTokenWriter()); - return asyncRequest; - } - - /** - * Initializes a new {@link LiveConnectSession} with the given scopes. - * - * The {@link LiveConnectSession} will be returned by calling - * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)}. - * Otherwise, the {@link LiveAuthListener#onAuthError(LiveAuthException, Object)} will be - * called. These methods will be called on the main/UI thread. - * - * If the wl.offline_access scope is used, a refresh_token is stored in the given - * {@link Activity}'s {@link SharedPerfences}. - * - * This initialize will use the last successfully used scopes from either a login or initialize. - * - * @param listener called on either completion or error during the initialize process. - */ - public void initialize(LiveAuthListener listener) { - this.initialize(listener, null); - } - - /** - * Initializes a new {@link LiveConnectSession} with the given scopes. - * - * The {@link LiveConnectSession} will be returned by calling - * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)}. - * Otherwise, the {@link LiveAuthListener#onAuthError(LiveAuthException, Object)} will be - * called. These methods will be called on the main/UI thread. - * - * If the wl.offline_access scope is used, a refresh_token is stored in the given - * {@link Activity}'s {@link SharedPerfences}. - * - * This initialize will use the last successfully used scopes from either a login or initialize. - * - * @param listener called on either completion or error during the initialize process. - * @param userState arbitrary object that is used to determine the caller of the method. - */ - public void initialize(LiveAuthListener listener, Object userState) { - this.initialize(null, listener, userState); - } - - /** - * Logs in an user with the given scopes. - * - * login displays a {@link Dialog} that will prompt the - * user for a username and password, and ask for consent to use the given scopes. - * A {@link LiveConnectSession} will be returned by calling - * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)}. - * Otherwise, the {@link LiveAuthListener#onAuthError(LiveAuthException, Object)} will be - * called. These methods will be called on the main/UI thread. - * - * @param activity {@link Activity} instance to display the Login dialog on. - * @param scopes to initialize the {@link LiveConnectSession} with. - * See MSDN Live Connect - * Reference's Scopes and permissions for a list of scopes and explanations. - * @param listener called on either completion or error during the login process. - * @throws IllegalStateException if there is a pending login request. - */ - public void login(Activity activity, Iterable scopes, LiveAuthListener listener) { - this.login(activity, scopes, listener, null); - } - - /** - * Logs in an user with the given scopes. - * - * login displays a {@link Dialog} that will prompt the - * user for a username and password, and ask for consent to use the given scopes. - * A {@link LiveConnectSession} will be returned by calling - * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)}. - * Otherwise, the {@link LiveAuthListener#onAuthError(LiveAuthException, Object)} will be - * called. These methods will be called on the main/UI thread. - * - * @param activity {@link Activity} instance to display the Login dialog on - * @param scopes to initialize the {@link LiveConnectSession} with. - * See MSDN Live Connect - * Reference's Scopes and permissions for a list of scopes and explanations. - * @param listener called on either completion or error during the login process. - * @param userState arbitrary object that is used to determine the caller of the method. - * @throws IllegalStateException if there is a pending login request. - */ - public void login(Activity activity, - Iterable scopes, - LiveAuthListener listener, - Object userState) { - LiveConnectUtils.assertNotNull(activity, "activity"); - - if (listener == null) { - listener = NULL_LISTENER; - } - - if (this.hasPendingLoginRequest) { - throw new IllegalStateException(ErrorMessages.LOGIN_IN_PROGRESS); - } - - // if no scopes were passed in, use the scopes from initialize or if those are empty, - // create an empty list - if (scopes == null) { - if (this.scopesFromInitialize == null) { - scopes = Arrays.asList(new String[0]); - } else { - scopes = this.scopesFromInitialize; - } - } - - // if the session is valid and contains all the scopes, do not display the login ui. - boolean showDialog = this.session.isExpired() || - !this.session.contains(scopes); - if (!showDialog) { - listener.onAuthComplete(LiveStatus.CONNECTED, this.session, userState); - return; - } - - String scope = TextUtils.join(OAuth.SCOPE_DELIMITER, scopes); - String redirectUri = Config.INSTANCE.getOAuthDesktopUri().toString(); - AuthorizationRequest request = new AuthorizationRequest(activity, - this.httpClient, - this.clientId, - redirectUri, - scope); - - request.addObserver(new ListenerCallerObserver(listener, userState)); - request.addObserver(new RefreshTokenWriter()); - request.addObserver(new OAuthRequestObserver() { - @Override - public void onException(LiveAuthException exception) { - LiveAuthClient.this.hasPendingLoginRequest = false; - } - - @Override - public void onResponse(OAuthResponse response) { - LiveAuthClient.this.hasPendingLoginRequest = false; - } - }); - - this.hasPendingLoginRequest = true; - - request.execute(); - } - - /** - * Logs out the given user. - * - * Also, this method clears the previously created {@link LiveConnectSession}. - * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)} will be - * called on completion. Otherwise, - * {@link LiveAuthListener#onAuthError(LiveAuthException, Object)} will be called. - * - * @param listener called on either completion or error during the logout process. - */ - public void logout(LiveAuthListener listener) { - this.logout(listener, null); - } - - /** - * Logs out the given user. - * - * Also, this method clears the previously created {@link LiveConnectSession}. - * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)} will be - * called on completion. Otherwise, - * {@link LiveAuthListener#onAuthError(LiveAuthException, Object)} will be called. - * - * @param listener called on either completion or error during the logout process. - * @param userState arbitrary object that is used to determine the caller of the method. - */ - public void logout(LiveAuthListener listener, Object userState) { - if (listener == null) { - listener = NULL_LISTENER; - } - - session.setAccessToken(null); - session.setAuthenticationToken(null); - session.setRefreshToken(null); - session.setScopes(null); - session.setTokenType(null); - - clearRefreshTokenFromPreferences(); - - CookieSyncManager cookieSyncManager = - CookieSyncManager.createInstance(this.applicationContext); - CookieManager manager = CookieManager.getInstance(); - Uri logoutUri = Config.INSTANCE.getOAuthLogoutUri(); - String url = logoutUri.toString(); - String domain = logoutUri.getHost(); - - List cookieKeys = this.getCookieKeysFromPreferences(); - for (String cookieKey : cookieKeys) { - String value = TextUtils.join("", new String[] { - cookieKey, - "=; expires=Thu, 30-Oct-1980 16:00:00 GMT;domain=", - domain, - ";path=/;version=1" - }); - - manager.setCookie(url, value); - } - - cookieSyncManager.sync(); - listener.onAuthComplete(LiveStatus.UNKNOWN, null, userState); - } - - /** @return The {@link HttpClient} instance used by this {@code LiveAuthClient}. */ - HttpClient getHttpClient() { - return this.httpClient; - } - - /** @return The {@link LiveConnectSession} instance that this {@code LiveAuthClient} created. */ - LiveConnectSession getSession() { - return session; - } - - /** - * Refreshes the previously created session. - * - * @return true if the session was successfully refreshed. - */ - boolean refresh() { - String scope = TextUtils.join(OAuth.SCOPE_DELIMITER, this.session.getScopes()); - String refreshToken = this.session.getRefreshToken(); - - if (TextUtils.isEmpty(refreshToken)) { - return false; - } - - RefreshAccessTokenRequest request = - new RefreshAccessTokenRequest(this.httpClient, this.clientId, refreshToken, scope); - - OAuthResponse response; - try { - response = request.execute(); - } catch (LiveAuthException e) { - return false; - } - - SessionRefresher refresher = new SessionRefresher(this.session); - response.accept(refresher); - response.accept(new RefreshTokenWriter()); - - return refresher.visitedSuccessfulResponse(); - } - - /** - * Sets the {@link HttpClient} that is used for HTTP requests by this {@code LiveAuthClient}. - * Tests will want to change this to mock the network/HTTP responses. - * @param client The new HttpClient to be set. - */ - void setHttpClient(HttpClient client) { - assert client != null; - this.httpClient = client; - } - - /** - * Clears the refresh token from this {@code LiveAuthClient}'s - * {@link Activity#getPreferences(int)}. - * - * @return true if the refresh token was successfully cleared. - */ - private boolean clearRefreshTokenFromPreferences() { - SharedPreferences settings = getSharedPreferences(); - Editor editor = settings.edit(); - editor.remove(PreferencesConstants.REFRESH_TOKEN_KEY); - - return editor.commit(); - } - - private SharedPreferences getSharedPreferences() { - return applicationContext.getSharedPreferences(PreferencesConstants.FILE_NAME, - Context.MODE_PRIVATE); - } - - private List getCookieKeysFromPreferences() { - SharedPreferences settings = getSharedPreferences(); - String cookieKeys = settings.getString(PreferencesConstants.COOKIES_KEY, ""); - - return Arrays.asList(TextUtils.split(cookieKeys, PreferencesConstants.COOKIE_DELIMITER)); - } - - /** - * Retrieves the refresh token from this {@code LiveAuthClient}'s - * {@link Activity#getPreferences(int)}. - * - * @return the refresh token from persistent storage. - */ - private String getRefreshTokenFromPreferences() { - SharedPreferences settings = getSharedPreferences(); - return settings.getString(PreferencesConstants.REFRESH_TOKEN_KEY, null); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveAuthException.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveAuthException.java deleted file mode 100644 index d9cd7e7c..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveAuthException.java +++ /dev/null @@ -1,63 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * Indicates that an exception occurred during the Auth process. - */ -public class LiveAuthException extends Exception { - - private static final long serialVersionUID = 3368677530670470856L; - - private final String error; - private final String errorUri; - - - LiveAuthException(String errorMessage) { - super(errorMessage); - this.error = ""; - this.errorUri = ""; - } - - LiveAuthException(String errorMessage, Throwable throwable) { - super(errorMessage, throwable); - this.error = ""; - this.errorUri = ""; - } - - LiveAuthException(String error, String errorDescription, String errorUri) { - super(errorDescription); - - assert error != null; - - this.error = error; - this.errorUri = errorUri; - } - - LiveAuthException(String error, String errorDescription, String errorUri, Throwable cause) { - super(errorDescription, cause); - - assert error != null; - - this.error = error; - this.errorUri = errorUri; - } - - /** - * @return Returns the authentication error. - */ - public String getError() { - return this.error; - } - - /** - * @return Returns the error URI. - */ - public String getErrorUri() { - return this.errorUri; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveAuthListener.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveAuthListener.java deleted file mode 100644 index e590910e..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveAuthListener.java +++ /dev/null @@ -1,33 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * Handles callback methods for LiveAuthClient init, login, and logout methods. - * Returns the * status of the operation when onAuthComplete is called. If there was an error - * during the operation, onAuthError is called with the exception that was thrown. - */ -public interface LiveAuthListener { - - /** - * Invoked when the operation completes successfully. - * - * @param status The {@link LiveStatus} for an operation. If successful, the status is - * CONNECTED. If unsuccessful, NOT_CONNECTED or UNKNOWN are returned. - * @param session The {@link LiveConnectSession} from the {@link LiveAuthClient}. - * @param userState An arbitrary object that is used to determine the caller of the method. - */ - public void onAuthComplete(LiveStatus status, LiveConnectSession session, Object userState); - - /** - * Invoked when the method call fails. - * - * @param exception The {@link LiveAuthException} error. - * @param userState An arbitrary object that is used to determine the caller of the method. - */ - public void onAuthError(LiveAuthException exception, Object userState); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveConnectClient.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveConnectClient.java deleted file mode 100644 index 11110057..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveConnectClient.java +++ /dev/null @@ -1,1898 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpVersion; -import org.apache.http.client.HttpClient; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.params.ConnManagerParams; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.protocol.HTTP; -import org.json.JSONException; -import org.json.JSONObject; - -import android.os.AsyncTask; -import android.text.TextUtils; - -/** - * {@code LiveConnectClient} is a class that is responsible for making requests over to the - * Live Connect REST API. In order to perform requests, a {@link LiveConnectSession} is required. - * A {@link LiveConnectSession} can be created from a {@link LiveAuthClient}. - * - * {@code LiveConnectClient} provides methods to perform both synchronous and asynchronous calls - * on the Live Connect REST API. A synchronous method's corresponding asynchronous method is - * suffixed with "Async" (e.g., the synchronous method, get, has a corresponding asynchronous - * method called, getAsync). Asynchronous methods require a call back listener that will be called - * back on the main/UI thread on completion, error, or progress. - */ -public class LiveConnectClient { - - /** Gets the ContentLength when a request finishes and sets it in the given operation. */ - private static class ContentLengthObserver implements ApiRequest.Observer { - private final LiveDownloadOperation operation; - - public ContentLengthObserver(LiveDownloadOperation operation) { - assert operation != null; - - this.operation = operation; - } - - @Override - public void onComplete(HttpResponse response) { - Header header = response.getFirstHeader(HTTP.CONTENT_LEN); - - // Sometimes this header is not included in the response. - if (header == null) { - return; - } - - int contentLength = Integer.valueOf(header.getValue()); - - this.operation.setContentLength(contentLength); - } - } - - /** - * Listens to an {@link ApiRequestAsync} for onComplete and onError events and calls the proper - * method on the given {@link LiveDownloadOperationListener} on a given event. - */ - private static class DownloadObserver implements ApiRequestAsync.Observer { - private final LiveDownloadOperationListener listener; - private final LiveDownloadOperation operation; - - public DownloadObserver(LiveDownloadOperation operation, - LiveDownloadOperationListener listener) { - assert operation != null; - assert listener != null; - - this.operation = operation; - this.listener = listener; - } - - @Override - public void onComplete(InputStream result) { - this.operation.setStream(result); - this.listener.onDownloadCompleted(this.operation); - } - - @Override - public void onError(LiveOperationException e) { - this.listener.onDownloadFailed(e, this.operation); - } - } - - /** - * Listens to an {@link ApiRequestAsync} for onComplete and onError events and calls the proper - * method on the given {@link LiveDownloadOperationListener} on a given event. When the download - * is complete this writes the results to a file, and publishes progress updates. - */ - private static class FileDownloadObserver extends AsyncTask - implements ApiRequestAsync.Observer { - private class OnErrorRunnable implements Runnable { - private final LiveOperationException exception; - - public OnErrorRunnable(LiveOperationException exception) { - this.exception = exception; - } - - @Override - public void run() { - listener.onDownloadFailed(exception, operation); - } - } - - private final File file; - private final LiveDownloadOperationListener listener; - private final LiveDownloadOperation operation; - - public FileDownloadObserver(LiveDownloadOperation operation, - LiveDownloadOperationListener listener, - File file) { - assert operation != null; - assert listener != null; - assert file != null; - - this.operation = operation; - this.listener = listener; - this.file = file; - } - - @Override - protected Runnable doInBackground(InputStream... params) { - InputStream is = params[0]; - - byte[] buffer = new byte[BUFFER_SIZE]; - - OutputStream out; - try { - out = new BufferedOutputStream(new FileOutputStream(file)); - } catch (FileNotFoundException e) { - LiveOperationException exception = - new LiveOperationException(ErrorMessages.CLIENT_ERROR, e); - return new OnErrorRunnable(exception); - } - - try { - int totalBytes = operation.getContentLength(); - int bytesRemaining = totalBytes; - - int bytesRead; - while ((bytesRead = is.read(buffer)) != -1) { - out.write(buffer, 0, bytesRead); - - bytesRemaining -= bytesRead; - publishProgress(totalBytes, bytesRemaining); - } - } catch (IOException e) { - LiveOperationException exception = - new LiveOperationException(ErrorMessages.CLIENT_ERROR, e); - return new OnErrorRunnable(exception); - } finally { - closeSilently(out); - closeSilently(is); - } - - return new Runnable() { - @Override - public void run() { - listener.onDownloadCompleted(operation); - } - }; - } - - @Override - protected void onPostExecute(Runnable result) { - result.run(); - } - - @Override - protected void onProgressUpdate(Integer... values) { - int totalBytes = values[0]; - int bytesRemaining = values[1]; - - assert totalBytes >= 0; - assert bytesRemaining >= 0; - assert totalBytes >= bytesRemaining; - - listener.onDownloadProgress(totalBytes, bytesRemaining, operation); - } - - @Override - public void onComplete(InputStream result) { - this.execute(result); - } - - @Override - public void onError(LiveOperationException e) { - this.listener.onDownloadFailed(e, this.operation); - } - } - - /** - * Listens to an {@link ApiRequestAsync} for onComplete and onError events and calls the proper - * method on the given {@link LiveOperationListener} on a given event. - */ - private static class OperationObserver implements ApiRequestAsync.Observer { - - private final LiveOperationListener listener; - private final LiveOperation operation; - - public OperationObserver(LiveOperation operation, - LiveOperationListener listener) { - assert operation != null; - assert listener != null; - - this.operation = operation; - this.listener = listener; - } - - @Override - public void onComplete(JSONObject result) { - this.operation.setResult(result); - this.listener.onComplete(this.operation); - } - - @Override - public void onError(LiveOperationException e) { - this.listener.onError(e, this.operation); - } - } - - /** non-instantiable class that contains static constants for parameter names. */ - private static final class ParamNames { - public static final String ACCESS_TOKEN = "session.getAccessToken()"; - public static final String BODY = "body"; - public static final String DESTINATION = "destination"; - public static final String FILE = "file"; - public static final String FILENAME = "filename"; - public static final String OVERWRITE = "overwrite"; - public static final String PATH = "path"; - public static final String SESSION = "session"; - - private ParamNames() { throw new AssertionError(ErrorMessages.NON_INSTANTIABLE_CLASS); } - } - - private enum SessionState { - LOGGED_IN { - @Override - public void check() { - // nothing. valid state. - } - }, - LOGGED_OUT { - @Override - public void check() { - throw new IllegalStateException(ErrorMessages.LOGGED_OUT); - } - }; - - public abstract void check(); - } - - /** - * Listens to an {@link ApiRequestAsync} for onComplete and onError events, and listens to an - * {@link EntityEnclosingApiRequest} for onProgress events and calls the - * proper {@link LiveUploadOperationListener} on such events. - */ - private static class UploadRequestListener implements ApiRequestAsync.Observer, - ApiRequestAsync.ProgressObserver { - - private final LiveUploadOperationListener listener; - private final LiveOperation operation; - - public UploadRequestListener(LiveOperation operation, - LiveUploadOperationListener listener) { - assert operation != null; - assert listener != null; - - this.operation = operation; - this.listener = listener; - } - - @Override - public void onComplete(JSONObject result) { - this.operation.setResult(result); - this.listener.onUploadCompleted(this.operation); - } - - @Override - public void onError(LiveOperationException e) { - assert e != null; - - this.listener.onUploadFailed(e, this.operation); - } - - @Override - public void onProgress(Long... values) { - long totalBytes = values[0].longValue(); - long numBytesWritten = values[1].longValue(); - - assert totalBytes >= 0L; - assert numBytesWritten >= 0L; - assert numBytesWritten <= totalBytes; - - long bytesRemaining = totalBytes - numBytesWritten; - this.listener.onUploadProgress((int)totalBytes, (int)bytesRemaining, this.operation); - } - } - - private static int BUFFER_SIZE = 1 << 10; - private static int CONNECT_TIMEOUT_IN_MS = 30 * 1000; - - /** The key used for HTTP MOVE and HTTP COPY requests. */ - private static final String DESTINATION_KEY = "destination"; - - private static volatile HttpClient HTTP_CLIENT; - private static Object HTTP_CLIENT_LOCK = new Object(); - - /** - * A LiveDownloadOperationListener that does nothing on each of the call backs. - * This is used so when a null listener is passed in, this can be used, instead of null, - * to avoid if (listener == null) checks. - */ - private static final LiveDownloadOperationListener NULL_DOWNLOAD_OPERATION_LISTENER; - - /** - * A LiveOperationListener that does nothing on each of the call backs. - * This is used so when a null listener is passed in, this can be used, instead of null, - * to avoid if (listener == null) checks. - */ - private static final LiveOperationListener NULL_OPERATION_LISTENER; - - /** - * A LiveUploadOperationListener that does nothing on each of the call backs. - * This is used so when a null listener is passed in, this can be used, instead of null, - * to avoid if (listener == null) checks. - */ - private static final LiveUploadOperationListener NULL_UPLOAD_OPERATION_LISTENER; - - private static int SOCKET_TIMEOUT_IN_MS = 30 * 1000; - - static { - NULL_DOWNLOAD_OPERATION_LISTENER = new LiveDownloadOperationListener() { - @Override - public void onDownloadCompleted(LiveDownloadOperation operation) { - assert operation != null; - } - - @Override - public void onDownloadFailed(LiveOperationException exception, - LiveDownloadOperation operation) { - assert exception != null; - assert operation != null; - } - - @Override - public void onDownloadProgress(int totalBytes, - int bytesRemaining, - LiveDownloadOperation operation) { - assert totalBytes >= 0; - assert bytesRemaining >= 0; - assert totalBytes >= bytesRemaining; - assert operation != null; - } - }; - - NULL_OPERATION_LISTENER = new LiveOperationListener() { - @Override - public void onComplete(LiveOperation operation) { - assert operation != null; - } - - @Override - public void onError(LiveOperationException exception, LiveOperation operation) { - assert exception != null; - assert operation != null; - } - }; - - NULL_UPLOAD_OPERATION_LISTENER = new LiveUploadOperationListener() { - @Override - public void onUploadCompleted(LiveOperation operation) { - assert operation != null; - } - - @Override - public void onUploadFailed(LiveOperationException exception, - LiveOperation operation) { - assert exception != null; - assert operation != null; - } - - @Override - public void onUploadProgress(int totalBytes, - int bytesRemaining, - LiveOperation operation) { - assert totalBytes >= 0; - assert bytesRemaining >= 0; - assert totalBytes >= bytesRemaining; - assert operation != null; - } - }; - } - - /** - * Checks to see if the given path is a valid uri. - * - * @param path to check. - * @return the valid URI object. - */ - private static URI assertIsUri(String path) { - try { - return new URI(path); - } catch (URISyntaxException e) { - String message = String.format(ErrorMessages.INVALID_URI, ParamNames.PATH); - throw new IllegalArgumentException(message); - } - } - - /** - * Checks to see if the path is null, empty, or a valid uri. - * - * This method will be used for Download and Upload requests. - * This method will NOT be used for Copy, Delete, Get, Move, Post and Put requests. - * - * @param path object_id to check. - * @throws IllegalArgumentException if the path is empty or an invalid uri. - * @throws NullPointerException if the path is null. - */ - private static void assertValidPath(String path) { - LiveConnectUtils.assertNotNullOrEmpty(path, ParamNames.PATH); - assertIsUri(path); - } - - private static void closeSilently(Closeable c) { - try { - c.close(); - } catch (Exception e) { - // Silently...ssshh - } - } - - /** - * Checks to see if the path is null, empty, or is an absolute uri and throws - * the proper exception if it is. - * - * This method will be used for Copy, Delete, Get, Move, Post, and Put requests. - * This method will NOT be used for Download and Upload requests. - * - * @param path object_id to check. - * @throws IllegalArgumentException if the path is empty or an absolute uri. - * @throws NullPointerException if the path is null. - */ - private static void assertValidRelativePath(String path) { - LiveConnectUtils.assertNotNullOrEmpty(path, ParamNames.PATH); - - if (path.toLowerCase().startsWith("http") || path.toLowerCase().startsWith("https")) { - String message = String.format(ErrorMessages.ABSOLUTE_PARAMETER, ParamNames.PATH); - throw new IllegalArgumentException(message); - } - } - - /** - * Creates a new JSONObject body that has one key-value pair. - * @param key - * @param value - * @return a new JSONObject body with one key-value pair. - */ - private static JSONObject createJsonBody(String key, String value) { - Map tempBody = new HashMap(); - tempBody.put(key, value); - return new JSONObject(tempBody); - } - - private static HttpClient getHttpClient() { - // The LiveConnectClients can share one HttpClient with a ThreadSafeConnManager. - if (HTTP_CLIENT == null) { - synchronized (HTTP_CLIENT_LOCK) { - if (HTTP_CLIENT == null) { - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setConnectionTimeout(params, CONNECT_TIMEOUT_IN_MS); - HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT_IN_MS); - - ConnManagerParams.setMaxTotalConnections(params, 100); - HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); - - SchemeRegistry schemeRegistry = new SchemeRegistry(); - schemeRegistry.register(new Scheme("http", - PlainSocketFactory.getSocketFactory(), - 80)); - schemeRegistry.register(new Scheme("https", - SSLSocketFactory.getSocketFactory(), - 443)); - - // Create an HttpClient with the ThreadSafeClientConnManager. - // This connection manager must be used if more than one thread will - // be using the HttpClient, which is a common scenario. - ClientConnectionManager cm = - new ThreadSafeClientConnManager(params, schemeRegistry); - HTTP_CLIENT = new DefaultHttpClient(cm, params); - } - } - } - - return HTTP_CLIENT; - } - - /** - * Constructs a new LiveOperation and calls the listener's onError method. - * - * @param e - * @param listener - * @param userState arbitrary object that is used to determine the caller of the method. - * @return a new LiveOperation - */ - private static LiveOperation handleException(String method, - String path, - LiveOperationException e, - LiveOperationListener listener, - Object userState) { - LiveOperation operation = - new LiveOperation.Builder(method, path).userState(userState).build(); - OperationObserver requestListener = - new OperationObserver(operation, listener); - - requestListener.onError(e); - return operation; - } - - /** - * Constructs a new LiveOperation and calls the listener's onUploadFailed method. - * - * @param e - * @param listener - * @param userState arbitrary object that is used to determine the caller of the method. - * @return a new LiveOperation - */ - private static LiveOperation handleException(String method, - String path, - LiveOperationException e, - LiveUploadOperationListener listener, - Object userState) { - LiveOperation operation = - new LiveOperation.Builder(method, path).userState(userState).build(); - UploadRequestListener requestListener = new UploadRequestListener(operation, listener); - - requestListener.onError(e); - return operation; - } - - /** - * Converts an InputStream to a {@code byte[]}. - * - * @param is to convert to a {@code byte[]}. - * @return a new {@code byte[]} from the InputStream. - * @throws IOException if there was an error reading or closing the InputStream. - */ - private static byte[] toByteArray(InputStream is) throws IOException { - ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - OutputStream out = new BufferedOutputStream(byteOut); - is = new BufferedInputStream(is); - byte[] buffer = new byte[BUFFER_SIZE]; - - try { - int bytesRead; - while ((bytesRead = is.read(buffer)) != -1) { - out.write(buffer, 0, bytesRead); - } - } finally { - // we want to perform silent close operations - closeSilently(is); - closeSilently(out); - } - - return byteOut.toByteArray(); - } - - /** Change this to mock the HTTP responses. */ - private HttpClient httpClient; - - private final LiveConnectSession session; - private SessionState sessionState; - - /** - * Constructs a new {@code LiveConnectClient} instance and initializes it. - * - * @param session that will be used to authenticate calls over to the Live Connect REST API. - * @throws NullPointerException if session is null or if session.getAccessToken() is null. - * @throws IllegalArgumentException if session.getAccessToken() is empty. - */ - public LiveConnectClient(LiveConnectSession session) { - LiveConnectUtils.assertNotNull(session, ParamNames.SESSION); - - String accessToken = session.getAccessToken(); - LiveConnectUtils.assertNotNullOrEmpty(accessToken, ParamNames.ACCESS_TOKEN); - - this.session = session; - this.sessionState = SessionState.LOGGED_IN; - - // set a listener for the accessToken. If it is set to null, then the session was logged - // out. - this.session.addPropertyChangeListener("accessToken", new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent event) { - String newValue = (String)event.getNewValue(); - - if (TextUtils.isEmpty(newValue)) { - LiveConnectClient.this.sessionState = SessionState.LOGGED_OUT; - } else { - LiveConnectClient.this.sessionState = SessionState.LOGGED_IN; - } - } - }); - - this.httpClient = getHttpClient(); - } - - /** - * Performs a synchronous HTTP COPY on the Live Connect REST API. - * - * A COPY duplicates a resource. - * - * @param path object_id of the resource to copy. - * @param destination the folder_id where the resource will be copied to. - * @return The LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - * @throws IllegalArgumentException if the path or destination is empty or if the path is an - * absolute uri. - * @throws NullPointerException if either the path or destination parameters are null. - */ - public LiveOperation copy(String path, String destination) throws LiveOperationException { - assertValidRelativePath(path); - LiveConnectUtils.assertNotNullOrEmpty(destination, ParamNames.DESTINATION); - - CopyRequest request = this.createCopyRequest(path, destination); - return execute(request); - } - - /** - * Performs an asynchronous HTTP COPY on the Live Connect REST API. - * - * A COPY duplicates a resource. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the resource to copy. - * @param destination the folder_id where the resource will be copied to. - * @param listener called on either completion or error during the copy request. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path or destination is empty or if the path is an - * absolute uri. - * @throws NullPointerException if either the path or destination parameters are null. - */ - public LiveOperation copyAsync(String path, - String destination, - LiveOperationListener listener) { - return this.copyAsync(path, destination, listener, null); - } - - /** - * Performs an asynchronous HTTP COPY on the Live Connect REST API. - * - * A COPY duplicates a resource. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the resource to copy. - * @param destination the folder_id where the resource will be copied to - * @param listener called on either completion or error during the copy request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path or destination is empty or if the path is an - * absolute uri. - * @throws NullPointerException if either the path or destination parameters are null. - */ - public LiveOperation copyAsync(String path, - String destination, - LiveOperationListener listener, - Object userState) { - assertValidRelativePath(path); - LiveConnectUtils.assertNotNullOrEmpty(destination, ParamNames.DESTINATION); - if (listener == null) { - listener = NULL_OPERATION_LISTENER; - } - - CopyRequest request; - try { - request = this.createCopyRequest(path, destination); - } catch (LiveOperationException e) { - return handleException(CopyRequest.METHOD, path, e, listener, userState); - } - - return executeAsync(request, listener, userState); - } - - /** - * Performs a synchronous HTTP DELETE on the Live Connect REST API. - * - * HTTP DELETE deletes a resource. - * - * @param path object_id of the resource to delete. - * @return The LiveOperation that contains the delete response - * @throws LiveOperationException if there is an error during the execution of the request. - * @throws IllegalArgumentException if the path is empty or an absolute uri. - * @throws NullPointerException if the path is null. - */ - public LiveOperation delete(String path) throws LiveOperationException { - assertValidRelativePath(path); - - DeleteRequest request = new DeleteRequest(this.session, this.httpClient, path); - - return execute(request); - } - - /** - * Performs an asynchronous HTTP DELETE on the Live Connect REST API. - * - * HTTP DELETE deletes a resource. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the resource to delete. - * @param listener called on either completion or error during the delete request. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or an absolute uri. - * @throws NullPointerException if the path is null. - */ - public LiveOperation deleteAsync(String path, LiveOperationListener listener) { - return this.deleteAsync(path, listener, null); - } - - /** - * Performs an asynchronous HTTP DELETE on the Live Connect REST API. - * - * HTTP DELETE deletes a resource. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the resource to delete. - * @param listener called on either completion or error during the delete request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or an absolute uri. - * @throws NullPointerException if the path is null. - */ - public LiveOperation deleteAsync(String path, - LiveOperationListener listener, - Object userState) { - assertValidRelativePath(path); - if (listener == null) { - listener = NULL_OPERATION_LISTENER; - } - - DeleteRequest request = new DeleteRequest(this.session, this.httpClient, path); - - - return executeAsync(request, listener, userState); - } - - /** - * Downloads a resource by performing a synchronous HTTP GET on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * @param path object_id of the resource to download. - * @throws LiveOperationException if there is an error during the execution of the request. - * @throws IllegalArgumentException if the path is empty or an invalid uri. - * @throws NullPointerException if the path is null. - */ - public LiveDownloadOperation download(String path) throws LiveOperationException { - assertValidPath(path); - - DownloadRequest request = new DownloadRequest(this.session, this.httpClient, path); - - LiveDownloadOperation operation = - new LiveDownloadOperation.Builder(request.getMethod(), request.getPath()).build(); - - request.addObserver(new ContentLengthObserver(operation)); - - InputStream stream = request.execute(); - operation.setStream(stream); - - return operation; - } - - /** - * Downloads a resource by performing an asynchronous HTTP GET on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * {@link LiveDownloadOperationListener#onDownloadCompleted(LiveDownloadOperation)} will be - * called on success. - * On any download progress - * {@link LiveDownloadOperationListener#onDownloadProgress(int, int, LiveDownloadOperation)} - * will be called. - * Otherwise on error, - * {@link LiveDownloadOperationListener#onDownloadFailed(LiveOperationException, - * LiveDownloadOperation)} will - * be called. All of these methods will be called on the main/UI thread. - * - * @param path object_id of the resource to download. - * @param listener called on either completion or error during the download request. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or an invalid uri. - * @throws NullPointerException if the path is null. - */ - public LiveDownloadOperation downloadAsync(String path, - LiveDownloadOperationListener listener) { - return this.downloadAsync(path, listener, null); - } - - /** - * Downloads a resource by performing an asynchronous HTTP GET on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * {@link LiveDownloadOperationListener#onDownloadCompleted(LiveDownloadOperation)} will be - * called on success. - * On any download progress - * {@link LiveDownloadOperationListener#onDownloadProgress(int, int, LiveDownloadOperation)} - * will be called. - * Otherwise on error, - * {@link LiveDownloadOperationListener#onDownloadFailed(LiveOperationException, - * LiveDownloadOperation)} will - * be called. All of these methods will be called on the main/UI thread. - * - * @param path object_id of the resource to download. - * @param listener called on either completion or error during the download request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or an invalid uri. - * @throws NullPointerException if the path is null. - */ - public LiveDownloadOperation downloadAsync(String path, - LiveDownloadOperationListener listener, - Object userState) { - assertValidPath(path); - if (listener == null) { - listener = NULL_DOWNLOAD_OPERATION_LISTENER; - } - - DownloadRequest request = new DownloadRequest(this.session, this.httpClient, path); - return executeAsync(request, listener, userState); - } - - public LiveDownloadOperation downloadAsync(String path, - File file, - LiveDownloadOperationListener listener) { - return this.downloadAsync(path, file, listener, null); - } - - public LiveDownloadOperation downloadAsync(String path, - File file, - LiveDownloadOperationListener listener, - Object userState) { - assertValidPath(path); - if (listener == null) { - listener = NULL_DOWNLOAD_OPERATION_LISTENER; - } - - DownloadRequest request = new DownloadRequest(this.session, this.httpClient, path); - ApiRequestAsync asyncRequest = ApiRequestAsync.newInstance(request); - - LiveDownloadOperation operation = - new LiveDownloadOperation.Builder(request.getMethod(), request.getPath()) - .userState(userState) - .apiRequestAsync(asyncRequest) - .build(); - - request.addObserver(new ContentLengthObserver(operation)); - asyncRequest.addObserver(new FileDownloadObserver(operation, listener, file)); - - asyncRequest.execute(); - - return operation; - } - - /** - * Performs a synchronous HTTP GET on the Live Connect REST API. - * - * HTTP GET retrieves the representation of a resource. - * - * @param path object_id of the resource to retrieve. - * @return The LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - * @throws IllegalArgumentException if the path is empty or an absolute uri. - * @throws NullPointerException if the path is null. - */ - public LiveOperation get(String path) throws LiveOperationException { - assertValidRelativePath(path); - - GetRequest request = new GetRequest(this.session, this.httpClient, path); - return execute(request); - } - - /** - * Performs an asynchronous HTTP GET on the Live Connect REST API. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path of the resource to retrieve. - * @param listener called on either completion or error during the get request. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or an absolute uri. - * @throws NullPointerException if the path is null. - */ - public LiveOperation getAsync(String path, LiveOperationListener listener) { - return this.getAsync(path, listener, null); - } - - /** - * Performs an asynchronous HTTP GET on the Live Connect REST API. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the resource to retrieve. - * @param listener called on either completion or error during the get request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or an absolute uri. - * @throws NullPointerException if the path is null. - */ - public LiveOperation getAsync(String path, LiveOperationListener listener, Object userState) { - assertValidRelativePath(path); - if (listener == null) { - listener = NULL_OPERATION_LISTENER; - } - - GetRequest request = new GetRequest(this.session, this.httpClient, path); - return executeAsync(request, listener, userState); - } - - /** @return the {@link LiveConnectSession} instance used by this {@code LiveConnectClient}. */ - public LiveConnectSession getSession() { - return this.session; - } - - /** - * Performs a synchronous HTTP MOVE on the Live Connect REST API. - * - * A MOVE moves the location of a resource. - * - * @param path object_id of the resource to move. - * @param destination the folder_id to where the resource will be moved to. - * @return The LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - * @throws IllegalArgumentException if the path or destination is empty or if the path is an - * absolute uri. - * @throws NullPointerException if either the path or destination parameters are null. - */ - public LiveOperation move(String path, String destination) throws LiveOperationException { - assertValidRelativePath(path); - LiveConnectUtils.assertNotNullOrEmpty(destination, ParamNames.DESTINATION); - - MoveRequest request = this.createMoveRequest(path, destination); - return execute(request); - } - - /** - * Performs an asynchronous HTTP MOVE on the Live Connect REST API. - * - * A MOVE moves the location of a resource. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the resource to move. - * @param destination the folder_id to where the resource will be moved to. - * @param listener called on either completion or error during the copy request. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path or destination is empty or if the path is an - * absolute uri. - * @throws NullPointerException if either the path or destination parameters are null. - */ - public LiveOperation moveAsync(String path, - String destination, - LiveOperationListener listener) { - return this.moveAsync(path, destination, listener, null); - } - - /** - * Performs an asynchronous HTTP MOVE on the Live Connect REST API. - * - * A MOVE moves the location of a resource. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the resource to move. - * @param destination the folder_id to where the resource will be moved to. - * @param listener called on either completion or error during the copy request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path or destination is empty or if the path is an - * absolute uri. - * @throws NullPointerException if either the path or destination parameters are null. - */ - public LiveOperation moveAsync(String path, - String destination, - LiveOperationListener listener, - Object userState) { - assertValidRelativePath(path); - LiveConnectUtils.assertNotNullOrEmpty(destination, ParamNames.DESTINATION); - if (listener == null) { - listener = NULL_OPERATION_LISTENER; - } - - MoveRequest request; - try { - request = this.createMoveRequest(path, destination); - } catch (LiveOperationException e) { - return handleException(MoveRequest.METHOD, path, e, listener, userState); - } - - return executeAsync(request, listener, userState); - } - - /** - * Performs a synchronous HTTP POST on the Live Connect REST API. - * - * A POST adds a new resource to a collection. - * - * @param path object_id of the post request. - * @param body body of the post request. - * @return a LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation post(String path, JSONObject body) throws LiveOperationException { - assertValidRelativePath(path); - LiveConnectUtils.assertNotNull(body, ParamNames.BODY); - - PostRequest request = createPostRequest(path, body); - return execute(request); - } - - /** - * Performs a synchronous HTTP POST on the Live Connect REST API. - * - * A POST adds a new resource to a collection. - * - * @param path object_id of the post request. - * @param body body of the post request. - * @return a LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation post(String path, String body) throws LiveOperationException { - LiveConnectUtils.assertNotNullOrEmpty(body, ParamNames.BODY); - - JSONObject jsonBody; - try { - jsonBody = new JSONObject(body.toString()); - } catch (JSONException e) { - throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e); - } - - return this.post(path, jsonBody); - } - - /** - * Performs an asynchronous HTTP POST on the Live Connect REST API. - * - * A POST adds a new resource to a collection. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the post request. - * @param body body of the post request. - * @param listener called on either completion or error during the copy request. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation postAsync(String path, JSONObject body, LiveOperationListener listener) { - return this.postAsync(path, body, listener, null); - } - - /** - * Performs an asynchronous HTTP POST on the Live Connect REST API. - * - * A POST adds a new resource to a collection. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the post request. - * @param body body of the post request. - * @param listener called on either completion or error during the copy request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation postAsync(String path, - JSONObject body, - LiveOperationListener listener, - Object userState) { - assertValidRelativePath(path); - LiveConnectUtils.assertNotNull(body, ParamNames.BODY); - if (listener == null) { - listener = NULL_OPERATION_LISTENER; - } - - PostRequest request; - try { - request = createPostRequest(path, body); - } catch (LiveOperationException e) { - return handleException(PostRequest.METHOD, path, e, listener, userState); - } - - return executeAsync(request, listener, userState); - } - - /** - * Performs an asynchronous HTTP POST on the Live Connect REST API. - * - * A POST adds a new resource to a collection. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the post request. - * @param body body of the post request. - * @param listener called on either completion or error during the copy request. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation postAsync(String path, String body, LiveOperationListener listener) { - return this.postAsync(path, body, listener, null); - } - - /** - * Performs an asynchronous HTTP POST on the Live Connect REST API. - * - * A POST adds a new resource to a collection. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the post request. - * @param body body of the post request. - * @param listener called on either completion or error during the copy request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation postAsync(String path, - String body, - LiveOperationListener listener, - Object userState) { - LiveConnectUtils.assertNotNullOrEmpty(body, ParamNames.BODY); - - JSONObject jsonBody; - try { - jsonBody = new JSONObject(body.toString()); - } catch (JSONException e) { - return handleException(PostRequest.METHOD, - path, - new LiveOperationException(ErrorMessages.CLIENT_ERROR, e), - listener, - userState); - } - - return this.postAsync(path, jsonBody, listener, userState); - } - - /** - * Performs a synchronous HTTP PUT on the Live Connect REST API. - * - * A PUT updates a resource or if it does not exist, it creates a one. - * - * @param path object_id of the put request. - * @param body body of the put request. - * @return a LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation put(String path, JSONObject body) throws LiveOperationException { - assertValidRelativePath(path); - LiveConnectUtils.assertNotNull(body, ParamNames.BODY); - - PutRequest request = createPutRequest(path, body); - return execute(request); - } - - /** - * Performs a synchronous HTTP PUT on the Live Connect REST API. - * - * A PUT updates a resource or if it does not exist, it creates a one. - * - * @param path object_id of the put request. - * @param body body of the put request. - * @return a LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation put(String path, String body) throws LiveOperationException { - LiveConnectUtils.assertNotNullOrEmpty(body, ParamNames.BODY); - - JSONObject jsonBody; - try { - jsonBody = new JSONObject(body.toString()); - } catch (JSONException e) { - throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e); - } - - return this.put(path, jsonBody); - } - - /** - * Performs an asynchronous HTTP PUT on the Live Connect REST API. - * - * A PUT updates a resource or if it does not exist, it creates a one. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the put request. - * @param body body of the put request. - * @param listener called on either completion or error during the put request. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation putAsync(String path, JSONObject body, LiveOperationListener listener) { - return this.putAsync(path, body, listener, null); - } - - /** - * Performs an asynchronous HTTP PUT on the Live Connect REST API. - * - * A PUT updates a resource or if it does not exist, it creates a one. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path of the put request. - * @param body of the put request. - * @param listener called on either completion or error during the put request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation putAsync(String path, - JSONObject body, - LiveOperationListener listener, - Object userState) { - assertValidRelativePath(path); - LiveConnectUtils.assertNotNull(body, ParamNames.BODY); - if (listener == null) { - listener = NULL_OPERATION_LISTENER; - } - - PutRequest request; - try { - request = createPutRequest(path, body); - } catch (LiveOperationException e) { - return handleException(PutRequest.METHOD, path, e, listener, userState); - } - - return executeAsync(request, listener, userState); - } - - /** - * Performs an asynchronous HTTP PUT on the Live Connect REST API. - * - * A PUT updates a resource or if it does not exist, it creates a one. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the put request. - * @param body body of the put request. - * @param listener called on either completion or error during the put request. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation putAsync(String path, String body, LiveOperationListener listener) { - return this.putAsync(path, body, listener, null); - } - - /** - * Performs an asynchronous HTTP PUT on the Live Connect REST API. - * - * A PUT updates a resource or if it does not exist, it creates a one. - * - * {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success. - * Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will - * be called. Both of these methods will be called on the main/UI thread. - * - * @param path object_id of the put request. - * @param body body of the put request. - * @param listener called on either completion or error during the put request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - * @throws IllegalArgumentException if the path is empty or is an absolute uri. - * @throws NullPointerException if either the path or body parameters are null. - */ - public LiveOperation putAsync(String path, - String body, - LiveOperationListener listener, - Object userState) { - LiveConnectUtils.assertNotNullOrEmpty(body, ParamNames.BODY); - JSONObject jsonBody; - try { - jsonBody = new JSONObject(body.toString()); - } catch (JSONException e) { - return handleException(PutRequest.METHOD, - path, - new LiveOperationException(ErrorMessages.CLIENT_ERROR, e), - listener, - userState); - } - - return this.putAsync(path, jsonBody, listener, userState); - } - - /** - * Uploads a resource by performing a synchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * If a file with the same name exists the upload will fail. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param file contents of the upload. - * @return a LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - */ - public LiveOperation upload(String path, - String filename, - InputStream file) throws LiveOperationException { - return this.upload(path, filename, file, OverwriteOption.DoNotOverwrite); - } - - /** - * Uploads a resource by performing a synchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param file contents of the upload. - * @param overwrite specifies what to do when a file with the same name exists. - * @return a LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - */ - public LiveOperation upload(String path, - String filename, - InputStream file, - OverwriteOption overwrite) throws LiveOperationException { - assertValidPath(path); - LiveConnectUtils.assertNotNullOrEmpty(filename, ParamNames.FILENAME); - LiveConnectUtils.assertNotNull(file, ParamNames.FILE); - LiveConnectUtils.assertNotNull(overwrite, ParamNames.OVERWRITE); - - // Currently, the API Service does not support chunked uploads, - // so we must know the length of the InputStream, before we send it. - // Load the stream into memory to get the length. - byte[] bytes; - try { - bytes = LiveConnectClient.toByteArray(file); - } catch (IOException e) { - throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e); - } - - UploadRequest request = createUploadRequest(path, - filename, - new ByteArrayInputStream(bytes), - bytes.length, - overwrite); - return execute(request); - } - - /** - * Uploads a resource by performing a synchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * If a file with the same name exists the upload will fail. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param file contents of the upload. - * @return a LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - */ - public LiveOperation upload(String path, - String filename, - File file) throws LiveOperationException { - return this.upload(path, filename, file, OverwriteOption.DoNotOverwrite); - } - - /** - * Uploads a resource by performing a synchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param file contents of the upload. - * @param overwrite specifies what to do when a file with the same name exists. - * @return a LiveOperation that contains the JSON result. - * @throws LiveOperationException if there is an error during the execution of the request. - */ - public LiveOperation upload(String path, - String filename, - File file, - OverwriteOption overwrite) throws LiveOperationException { - assertValidPath(path); - LiveConnectUtils.assertNotNullOrEmpty(filename, ParamNames.FILENAME); - LiveConnectUtils.assertNotNull(file, ParamNames.FILE); - LiveConnectUtils.assertNotNull(overwrite, ParamNames.OVERWRITE); - - InputStream is = null; - try { - is = new FileInputStream(file); - } catch (FileNotFoundException e) { - throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e); - } - - UploadRequest request; - request = createUploadRequest(path, - filename, - is, - file.length(), - overwrite); - return execute(request); - } - - /** - * Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on - * success. - * {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called - * on upload progress. Both of these methods will be called on the main/UI thread. - * Otherwise, - * {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)} - * will be called. This method will NOT be called on the main/UI thread. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param file contents of the upload. - * @param overwrite specifies what to do when a file with the same name exists. - * @param listener called on completion, on progress, or on an error of the upload request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - */ - public LiveOperation uploadAsync(String path, - String filename, - InputStream file, - OverwriteOption overwrite, - LiveUploadOperationListener listener, - Object userState) { - assertValidPath(path); - LiveConnectUtils.assertNotNullOrEmpty(filename, ParamNames.FILENAME); - LiveConnectUtils.assertNotNull(file, ParamNames.FILE); - LiveConnectUtils.assertNotNull(overwrite, ParamNames.OVERWRITE); - if (listener == null) { - listener = NULL_UPLOAD_OPERATION_LISTENER; - } - - // Currently, the API Service does not support chunked uploads, - // so we must know the length of the InputStream, before we send it. - // Load the stream into memory to get the length. - byte[] bytes; - try { - bytes = LiveConnectClient.toByteArray(file); - } catch (IOException e) { - LiveOperationException exception = - new LiveOperationException(ErrorMessages.CLIENT_ERROR, e); - return handleException(UploadRequest.METHOD, path, exception, listener, userState); - } - - UploadRequest request; - try { - request = createUploadRequest(path, - filename, - new ByteArrayInputStream(bytes), - bytes.length, - overwrite); - } catch (LiveOperationException e) { - return handleException(UploadRequest.METHOD, path, e, listener, userState); - } - - ApiRequestAsync asyncRequest = ApiRequestAsync.newInstance(request); - - LiveOperation operation = new LiveOperation.Builder(request.getMethod(), request.getPath()) - .userState(userState) - .apiRequestAsync(asyncRequest) - .build(); - - UploadRequestListener operationListener = new UploadRequestListener(operation, listener); - - asyncRequest.addObserver(operationListener); - asyncRequest.addProgressObserver(operationListener); - asyncRequest.execute(); - - return operation; - } - - /** - * Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on - * success. - * {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called - * on upload progress. Both of these methods will be called on the main/UI thread. - * Otherwise, - * {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)} - * will be called. This method will NOT be called on the main/UI thread. - * - * If a file with the same name exists the upload will fail. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param input contents of the upload. - * @param listener called on completion, on progress, or on an error of the upload request. - * @return the LiveOperation associated with the request. - */ - public LiveOperation uploadAsync(String path, - String filename, - InputStream input, - LiveUploadOperationListener listener) { - return this.uploadAsync(path, filename, input, listener, null); - } - - /** - * Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on - * success. - * {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called - * on upload progress. Both of these methods will be called on the main/UI thread. - * Otherwise, - * {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)} - * will be called. This method will NOT be called on the main/UI thread. - * - * If a file with the same name exists the upload will fail. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param file contents of the upload. - * @param listener called on completion, on progress, or on an error of the upload request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - */ - public LiveOperation uploadAsync(String path, - String filename, - InputStream input, - LiveUploadOperationListener listener, - Object userState) { - return this.uploadAsync( - path, - filename, - input, - OverwriteOption.DoNotOverwrite, - listener, - userState); - } - - /** - * Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on - * success. - * {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called - * on upload progress. Both of these methods will be called on the main/UI thread. - * Otherwise, - * {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)} - * will be called. This method will NOT be called on the main/UI thread. - * - * If a file with the same name exists the upload will fail. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param file contents of the upload. - * @param listener called on completion, on progress, or on an error of the upload request. - * @return the LiveOperation associated with the request. - */ - public LiveOperation uploadAsync(String path, - String filename, - File file, - LiveUploadOperationListener listener) { - return this.uploadAsync(path, filename, file, listener, null); - } - - /** - * Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on - * success. - * {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called - * on upload progress. Both of these methods will be called on the main/UI thread. - * Otherwise, - * {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)} - * will be called. This method will NOT be called on the main/UI thread. - * - * If a file with the same name exists the upload will fail. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param file contents of the upload. - * @param listener called on completion, on progress, or on an error of the upload request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - */ - public LiveOperation uploadAsync(String path, - String filename, - File file, - LiveUploadOperationListener listener, - Object userState) { - return this.uploadAsync( - path, - filename, - file, - OverwriteOption.DoNotOverwrite, - listener, - userState); - } - - /** - * Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that - * returns the response as an {@link InputStream}. - * - * {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on - * success. - * {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called - * on upload progress. Both of these methods will be called on the main/UI thread. - * Otherwise, - * {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)} - * will be called. This method will NOT be called on the main/UI thread. - * - * @param path location to upload to. - * @param filename name of the new resource. - * @param file contents of the upload. - * @param overwrite specifies what to do when a file with the same name exists. - * @param listener called on completion, on progress, or on an error of the upload request. - * @param userState arbitrary object that is used to determine the caller of the method. - * @return the LiveOperation associated with the request. - */ - public LiveOperation uploadAsync(String path, - String filename, - File file, - OverwriteOption overwrite, - LiveUploadOperationListener listener, - Object userState) { - assertValidPath(path); - LiveConnectUtils.assertNotNullOrEmpty(filename, ParamNames.FILENAME); - LiveConnectUtils.assertNotNull(file, ParamNames.FILE); - LiveConnectUtils.assertNotNull(overwrite, ParamNames.OVERWRITE); - if (listener == null) { - listener = NULL_UPLOAD_OPERATION_LISTENER; - } - - UploadRequest request; - try { - request = createUploadRequest(path, - filename, - new FileInputStream(file), - file.length(), - overwrite); - } catch (LiveOperationException e) { - return handleException(UploadRequest.METHOD, path, e, listener, userState); - } catch (FileNotFoundException e) { - LiveOperationException exception = - new LiveOperationException(ErrorMessages.CLIENT_ERROR, e); - return handleException(UploadRequest.METHOD, path, exception, listener, userState); - } - - ApiRequestAsync asyncRequest = ApiRequestAsync.newInstance(request); - - LiveOperation operation = new LiveOperation.Builder(request.getMethod(), request.getPath()) - .userState(userState) - .apiRequestAsync(asyncRequest) - .build(); - - UploadRequestListener operationListener = new UploadRequestListener(operation, listener); - - asyncRequest.addObserver(operationListener); - asyncRequest.addProgressObserver(operationListener); - asyncRequest.execute(); - - return operation; - } - - /** - * Sets the HttpClient that is used in requests. - * - * This is here to be able to mock the server for testing purposes. - * - * @param client - */ - void setHttpClient(HttpClient client) { - assert client != null; - this.httpClient = client; - } - - /** - * Creates a {@link CopyRequest} and its json body. - * @param path location of the request. - * @param destination value for the json body. - * @return a new {@link CopyRequest}. - * @throws LiveOperationException if there is an error creating the request. - */ - private CopyRequest createCopyRequest(String path, - String destination) throws LiveOperationException { - assert !TextUtils.isEmpty(path); - assert !TextUtils.isEmpty(destination); - - JSONObject body = LiveConnectClient.createJsonBody(DESTINATION_KEY, destination); - HttpEntity entity = createJsonEntity(body); - return new CopyRequest(this.session, this.httpClient, path, entity); - } - - private JsonEntity createJsonEntity(JSONObject body) throws LiveOperationException { - assert body != null; - - try { - return new JsonEntity(body); - } catch (UnsupportedEncodingException e) { - throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e); - } - } - - private MoveRequest createMoveRequest(String path, - String destination) throws LiveOperationException { - assert !TextUtils.isEmpty(path); - assert !TextUtils.isEmpty(destination); - - JSONObject body = LiveConnectClient.createJsonBody(DESTINATION_KEY, destination); - HttpEntity entity = createJsonEntity(body); - return new MoveRequest(this.session, this.httpClient, path, entity); - } - - private PostRequest createPostRequest(String path, - JSONObject body) throws LiveOperationException { - assert !TextUtils.isEmpty(path); - assert body != null; - - HttpEntity entity = createJsonEntity(body); - return new PostRequest(this.session, this.httpClient, path, entity); - } - - private PutRequest createPutRequest(String path, - JSONObject body) throws LiveOperationException { - assert !TextUtils.isEmpty(path); - assert body != null; - - HttpEntity entity = createJsonEntity(body); - return new PutRequest(this.session, this.httpClient, path, entity); - } - - private UploadRequest createUploadRequest(String path, - String filename, - InputStream is, - long length, - OverwriteOption overwrite) throws LiveOperationException { - assert !TextUtils.isEmpty(path); - assert !TextUtils.isEmpty(filename); - assert is != null; - - InputStreamEntity entity = new InputStreamEntity(is, length); - - return new UploadRequest(this.session, this.httpClient, path, entity, filename, overwrite); - } - - /** - * Creates a new LiveOperation and executes it synchronously. - * - * @param request - * @param listener - * @param userState arbitrary object that is used to determine the caller of the method. - * @return a new LiveOperation. - */ - private LiveOperation execute(ApiRequest request) throws LiveOperationException { - this.sessionState.check(); - - JSONObject result = request.execute(); - - LiveOperation.Builder builder = - new LiveOperation.Builder(request.getMethod(), request.getPath()).result(result); - - return builder.build(); - } - - /** - * Creates a new LiveDownloadOperation and executes it asynchronously. - * - * @param request - * @param listener - * @param userState arbitrary object that is used to determine the caller of the method. - * @return a new LiveDownloadOperation. - */ - private LiveDownloadOperation executeAsync(ApiRequest request, - LiveDownloadOperationListener listener, - Object userState) { - this.sessionState.check(); - - ApiRequestAsync asyncRequest = ApiRequestAsync.newInstance(request); - - LiveDownloadOperation operation = - new LiveDownloadOperation.Builder(request.getMethod(), request.getPath()) - .userState(userState) - .apiRequestAsync(asyncRequest) - .build(); - - - request.addObserver(new ContentLengthObserver(operation)); - asyncRequest.addObserver(new DownloadObserver(operation, listener)); - asyncRequest.execute(); - - return operation; - } - - /** - * Creates a new LiveOperation and executes it asynchronously. - * - * @param request - * @param listener - * @param userState arbitrary object that is used to determine the caller of the method. - * @return a new LiveOperation. - */ - private LiveOperation executeAsync(ApiRequest request, - LiveOperationListener listener, - Object userState) { - this.sessionState.check(); - - ApiRequestAsync asyncRequest = ApiRequestAsync.newInstance(request); - - LiveOperation operation = new LiveOperation.Builder(request.getMethod(), request.getPath()) - .userState(userState) - .apiRequestAsync(asyncRequest) - .build(); - - asyncRequest.addObserver(new OperationObserver(operation, listener)); - asyncRequest.execute(); - - return operation; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveConnectSession.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveConnectSession.java deleted file mode 100644 index c14fff7b..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveConnectSession.java +++ /dev/null @@ -1,311 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -/** - * Represents a Live Connect session. - */ -public class LiveConnectSession { - - private String accessToken; - private String authenticationToken; - - /** Keeps track of all the listeners, and fires the property change events */ - private final PropertyChangeSupport changeSupport; - - /** - * The LiveAuthClient that created this object. - * This is needed in order to perform a refresh request. - * There is a one-to-one relationship between the LiveConnectSession and LiveAuthClient. - */ - private final LiveAuthClient creator; - - private Date expiresIn; - private String refreshToken; - private Set scopes; - private String tokenType; - - /** - * Constructors a new LiveConnectSession, and sets its creator to the passed in - * LiveAuthClient. All other member variables are left uninitialized. - * - * @param creator - */ - LiveConnectSession(LiveAuthClient creator) { - assert creator != null; - - this.creator = creator; - this.changeSupport = new PropertyChangeSupport(this); - } - - /** - * Adds a {@link PropertyChangeListener} to the session that receives notification when any - * property is changed. - * - * @param listener - */ - public void addPropertyChangeListener(PropertyChangeListener listener) { - if (listener == null) { - return; - } - - this.changeSupport.addPropertyChangeListener(listener); - } - - /** - * Adds a {@link PropertyChangeListener} to the session that receives notification when a - * specific property is changed. - * - * @param propertyName - * @param listener - */ - public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { - if (listener == null) { - return; - } - - this.changeSupport.addPropertyChangeListener(propertyName, listener); - } - - /** - * @return The access token for the signed-in, connected user. - */ - public String getAccessToken() { - return this.accessToken; - } - - /** - * @return A user-specific token that provides information to an app so that it can validate - * the user. - */ - public String getAuthenticationToken() { - return this.authenticationToken; - } - - /** - * @return The exact time when a session expires. - */ - public Date getExpiresIn() { - // Defensive copy - return new Date(this.expiresIn.getTime()); - } - - /** - * @return An array of all PropertyChangeListeners for this session. - */ - public PropertyChangeListener[] getPropertyChangeListeners() { - return this.changeSupport.getPropertyChangeListeners(); - } - - /** - * @param propertyName - * @return An array of all PropertyChangeListeners for a specific property for this session. - */ - public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { - return this.changeSupport.getPropertyChangeListeners(propertyName); - } - - /** - * @return A user-specific refresh token that the app can use to refresh the access token. - */ - public String getRefreshToken() { - return this.refreshToken; - } - - /** - * @return The scopes that the user has consented to. - */ - public Iterable getScopes() { - // Defensive copy is not necessary, because this.scopes is an unmodifiableSet - return this.scopes; - } - - /** - * @return The type of token. - */ - public String getTokenType() { - return this.tokenType; - } - - /** - * @return {@code true} if the session is expired. - */ - public boolean isExpired() { - if (this.expiresIn == null) { - return true; - } - - final Date now = new Date(); - - return now.after(this.expiresIn); - } - - /** - * Removes a PropertyChangeListeners on a session. - * @param listener - */ - public void removePropertyChangeListener(PropertyChangeListener listener) { - if (listener == null) { - return; - } - - this.changeSupport.removePropertyChangeListener(listener); - } - - /** - * Removes a PropertyChangeListener for a specific property on a session. - * @param propertyName - * @param listener - */ - public void removePropertyChangeListener(String propertyName, - PropertyChangeListener listener) { - if (listener == null) { - return; - } - - this.changeSupport.removePropertyChangeListener(propertyName, listener); - } - - @Override - public String toString() { - return String.format("LiveConnectSession [accessToken=%s, authenticationToken=%s, expiresIn=%s, refreshToken=%s, scopes=%s, tokenType=%s]", - this.accessToken, - this.authenticationToken, - this.expiresIn, - this.refreshToken, - this.scopes, - this.tokenType); - } - - boolean contains(Iterable scopes) { - if (scopes == null) { - return true; - } else if (this.scopes == null) { - return false; - } - - for (String scope : scopes) { - if (!this.scopes.contains(scope)) { - return false; - } - } - - return true; - } - - /** - * Fills in the LiveConnectSession with the OAuthResponse. - * WARNING: The OAuthResponse must not contain OAuth.ERROR. - * - * @param response to load from - */ - void loadFromOAuthResponse(OAuthSuccessfulResponse response) { - this.accessToken = response.getAccessToken(); - this.tokenType = response.getTokenType().toString().toLowerCase(); - - if (response.hasAuthenticationToken()) { - this.authenticationToken = response.getAuthenticationToken(); - } - - if (response.hasExpiresIn()) { - final Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.SECOND, response.getExpiresIn()); - this.setExpiresIn(calendar.getTime()); - } - - if (response.hasRefreshToken()) { - this.refreshToken = response.getRefreshToken(); - } - - if (response.hasScope()) { - final String scopeString = response.getScope(); - this.setScopes(Arrays.asList(scopeString.split(OAuth.SCOPE_DELIMITER))); - } - } - - /** - * Refreshes this LiveConnectSession - * - * @return true if it was able to refresh the refresh token. - */ - boolean refresh() { - return this.creator.refresh(); - } - - void setAccessToken(String accessToken) { - final String oldValue = this.accessToken; - this.accessToken = accessToken; - - this.changeSupport.firePropertyChange("accessToken", oldValue, this.accessToken); - } - - void setAuthenticationToken(String authenticationToken) { - final String oldValue = this.authenticationToken; - this.authenticationToken = authenticationToken; - - this.changeSupport.firePropertyChange("authenticationToken", - oldValue, - this.authenticationToken); - } - - void setExpiresIn(Date expiresIn) { - final Date oldValue = this.expiresIn; - this.expiresIn = new Date(expiresIn.getTime()); - - this.changeSupport.firePropertyChange("expiresIn", oldValue, this.expiresIn); - } - - void setRefreshToken(String refreshToken) { - final String oldValue = this.refreshToken; - this.refreshToken = refreshToken; - - this.changeSupport.firePropertyChange("refreshToken", oldValue, this.refreshToken); - } - - void setScopes(Iterable scopes) { - final Iterable oldValue = this.scopes; - - // Defensive copy - this.scopes = new HashSet(); - if (scopes != null) { - for (String scope : scopes) { - this.scopes.add(scope); - } - } - - this.scopes = Collections.unmodifiableSet(this.scopes); - - this.changeSupport.firePropertyChange("scopes", oldValue, this.scopes); - } - - void setTokenType(String tokenType) { - final String oldValue = this.tokenType; - this.tokenType = tokenType; - - this.changeSupport.firePropertyChange("tokenType", oldValue, this.tokenType); - } - - boolean willExpireInSecs(int secs) { - final Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.SECOND, secs); - - final Date future = calendar.getTime(); - - // if add secs seconds to the current time and it is after the expired time - // then it is almost expired. - return future.after(this.expiresIn); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveConnectUtils.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveConnectUtils.java deleted file mode 100644 index dc06e013..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveConnectUtils.java +++ /dev/null @@ -1,59 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - - -import android.text.TextUtils; - -/** - * LiveConnectUtils is a non-instantiable utility class that contains various helper - * methods and constants. - */ -final class LiveConnectUtils { - - /** - * Checks to see if the passed in Object is null, and throws a - * NullPointerException if it is. - * - * @param object to check - * @param parameterName name of the parameter that is used in the exception message - * @throws NullPointerException if the Object is null - */ - public static void assertNotNull(Object object, String parameterName) { - assert !TextUtils.isEmpty(parameterName); - - if (object == null) { - final String message = String.format(ErrorMessages.NULL_PARAMETER, parameterName); - throw new NullPointerException(message); - } - } - - /** - * Checks to see if the passed in is an empty string, and throws an - * IllegalArgumentException if it is. - * - * @param parameter to check - * @param parameterName name of the parameter that is used in the exception message - * @throws IllegalArgumentException if the parameter is empty - * @throws NullPointerException if the String is null - */ - public static void assertNotNullOrEmpty(String parameter, String parameterName) { - assert !TextUtils.isEmpty(parameterName); - - assertNotNull(parameter, parameterName); - - if (TextUtils.isEmpty(parameter)) { - final String message = String.format(ErrorMessages.EMPTY_PARAMETER, parameterName); - throw new IllegalArgumentException(message); - } - } - - /** - * Private to prevent instantiation - */ - private LiveConnectUtils() { throw new AssertionError(ErrorMessages.NON_INSTANTIABLE_CLASS); } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveDownloadOperation.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveDownloadOperation.java deleted file mode 100644 index db8ce356..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveDownloadOperation.java +++ /dev/null @@ -1,131 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.io.InputStream; - -import android.text.TextUtils; - -/** - * Represents data returned from a download call to the Live Connect Representational State - * Transfer (REST) API. - */ -public class LiveDownloadOperation { - static class Builder { - private ApiRequestAsync apiRequestAsync; - private final String method; - private final String path; - private InputStream stream; - private Object userState; - - public Builder(String method, String path) { - assert !TextUtils.isEmpty(method); - assert !TextUtils.isEmpty(path); - - this.method = method; - this.path = path; - } - - /** - * Set if the operation to build is an async operation. - * - * @param apiRequestAsync - * @return this Builder - */ - public Builder apiRequestAsync(ApiRequestAsync apiRequestAsync) { - assert apiRequestAsync != null; - - this.apiRequestAsync = apiRequestAsync; - return this; - } - - public LiveDownloadOperation build() { - return new LiveDownloadOperation(this); - } - - public Builder stream(InputStream stream) { - assert stream != null; - - this.stream = stream; - return this; - } - - public Builder userState(Object userState) { - this.userState = userState; - return this; - } - } - - private final ApiRequestAsync apiRequestAsync; - private int contentLength; - private final String method; - private final String path; - private InputStream stream; - private final Object userState; - - LiveDownloadOperation(Builder builder) { - this.apiRequestAsync = builder.apiRequestAsync; - this.method = builder.method; - this.path = builder.path; - this.stream = builder.stream; - this.userState = builder.userState; - } - - public void cancel() { - final boolean isCancelable = this.apiRequestAsync != null; - if (isCancelable) { - this.apiRequestAsync.cancel(true); - } - } - - /** - * @return The type of HTTP method used to make the call. - */ - public String getMethod() { - return this.method; - } - - /** - * @return The length of the stream. - */ - public int getContentLength() { - return this.contentLength; - } - - /** - * @return The path for the stream object. - */ - public String getPath() { - return this.path; - } - - /** - * @return The stream object that contains the downloaded file. - */ - public InputStream getStream() { - return this.stream; - } - - /** - * @return The user state. - */ - public Object getUserState() { - return this.userState; - } - - void setContentLength(int contentLength) { - assert contentLength >= 0; - - this.contentLength = contentLength; - } - - void setStream(InputStream stream) { - assert stream != null; - - this.stream = stream; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveDownloadOperationListener.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveDownloadOperationListener.java deleted file mode 100644 index 2d0348ef..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveDownloadOperationListener.java +++ /dev/null @@ -1,38 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * Represents any functionality related to downloads that works with the Live Connect - * Representational State Transfer (REST) API. - */ -public interface LiveDownloadOperationListener { - - /** - * Called when the associated download operation call completes. - * @param operation The {@link LiveDownloadOperation} object. - */ - public void onDownloadCompleted(LiveDownloadOperation operation); - - /** - * Called when the associated download operation call fails. - * @param exception The error returned by the REST operation call. - * @param operation The {@link LiveDownloadOperation} object. - */ - public void onDownloadFailed(LiveOperationException exception, - LiveDownloadOperation operation); - - /** - * Updates the progression of the download. - * @param totalBytes The total bytes downloaded. - * @param bytesRemaining The bytes remaining to download. - * @param operation The {@link LiveDownloadOperation} object. - */ - public void onDownloadProgress(int totalBytes, - int bytesRemaining, - LiveDownloadOperation operation); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveOperation.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveOperation.java deleted file mode 100644 index 6cc07484..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveOperation.java +++ /dev/null @@ -1,129 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import org.json.JSONObject; - -import android.text.TextUtils; - -/** - * Represents data returned from the Live Connect Representational State Transfer (REST) API - * services. - */ -public class LiveOperation { - static class Builder { - private ApiRequestAsync apiRequestAsync; - private final String method; - private final String path; - private JSONObject result; - private Object userState; - - public Builder(String method, String path) { - assert !TextUtils.isEmpty(method); - assert !TextUtils.isEmpty(path); - - this.method = method; - this.path = path; - } - - /** - * Set if the operation to build is an async operation. - * - * @param apiRequestAsync - * @return this Builder - */ - public Builder apiRequestAsync(ApiRequestAsync apiRequestAsync) { - assert apiRequestAsync != null; - - this.apiRequestAsync = apiRequestAsync; - return this; - } - - public LiveOperation build() { - return new LiveOperation(this); - } - - public Builder result(JSONObject result) { - assert result != null; - this.result = result; - return this; - } - - public Builder userState(Object userState) { - this.userState = userState; - return this; - } - } - - private final ApiRequestAsync apiRequestAsync; - private final String method; - private final String path; - private JSONObject result; - private final Object userState; - - private LiveOperation(Builder builder) { - this.apiRequestAsync = builder.apiRequestAsync; - this.method = builder.method; - this.path = builder.path; - this.result = builder.result; - this.userState = builder.userState; - } - - /** Cancels the pending request. */ - public void cancel() { - final boolean isCancelable = this.apiRequestAsync != null; - if (isCancelable) { - this.apiRequestAsync.cancel(true); - } - } - - /** - * @return The type of HTTP method used to make the call. - */ - public String getMethod() { - return this.method; - } - - /** - * @return The path to which the call was made. - */ - public String getPath() { - return this.path; - } - - /** - * @return The raw result of the operation in the requested format. - */ - public String getRawResult() { - JSONObject result = this.getResult(); - if (result == null) { - return null; - } - - return result.toString(); - } - - /** - * @return The JSON object that is the result of the requesting operation. - */ - public JSONObject getResult() { - return this.result; - } - - /** - * @return The user state that was passed in. - */ - public Object getUserState() { - return this.userState; - } - - void setResult(JSONObject result) { - assert result != null; - - this.result = result; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveOperationException.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveOperationException.java deleted file mode 100644 index db4904eb..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveOperationException.java +++ /dev/null @@ -1,24 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * Represents errors that occur when making requests to the Representational State Transfer - * (REST) API. - */ -public class LiveOperationException extends Exception { - - private static final long serialVersionUID = 4630383031651156731L; - - LiveOperationException(String message) { - super(message); - } - - LiveOperationException(String message, Throwable e) { - super(message, e); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveOperationListener.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveOperationListener.java deleted file mode 100644 index e3d6741e..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveOperationListener.java +++ /dev/null @@ -1,27 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * Called when an operation finishes or has an error. - */ -public interface LiveOperationListener { - - /** - * Called when the associated Representational State Transfer (REST) API operation call - * completes. - * @param operation The {@link LiveOperation} object. - */ - public void onComplete(LiveOperation operation); - - /** - * Called when the associated Representational State Transfer (REST) operation call fails. - * @param exception The error returned by the REST operation call. - * @param operation The {@link LiveOperation} object. - */ - public void onError(LiveOperationException exception, LiveOperation operation); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveStatus.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveStatus.java deleted file mode 100644 index ef4cad33..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveStatus.java +++ /dev/null @@ -1,21 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * Specifies the status of an auth operation. - */ -public enum LiveStatus { - /** The status is not known. */ - UNKNOWN, - - /** The session is connected. */ - CONNECTED, - - /** The user has not consented to the application. */ - NOT_CONNECTED; -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveUploadOperationListener.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveUploadOperationListener.java deleted file mode 100644 index 7da4f357..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/LiveUploadOperationListener.java +++ /dev/null @@ -1,35 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * Represents any functionality related to uploads that works with the Live Connect - * Representational State Transfer (REST) API. - */ -public interface LiveUploadOperationListener { - - /** - * Called when the associated upload operation call completes. - * @param operation The {@link LiveOperation} object. - */ - public void onUploadCompleted(LiveOperation operation); - - /** - * Called when the associated upload operation call fails. - * @param exception The error returned by the REST operation call. - * @param operation The {@link LiveOperation} object. - */ - public void onUploadFailed(LiveOperationException exception, LiveOperation operation); - - /** - * Called arbitrarily during the progress of the upload request. - * @param totalBytes The total bytes downloaded. - * @param bytesRemaining The bytes remaining to download. - * @param operation The {@link LiveOperation} object. - */ - public void onUploadProgress(int totalBytes, int bytesRemaining, LiveOperation operation); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/MoveRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/MoveRequest.java deleted file mode 100644 index a5e5908d..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/MoveRequest.java +++ /dev/null @@ -1,55 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import org.apache.http.HttpEntity; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpUriRequest; -import org.json.JSONObject; - -/** - * MoveRequest is a subclass of a BodyEnclosingApiRequest and performs a Move request. - */ -class MoveRequest extends EntityEnclosingApiRequest { - - public static final String METHOD = HttpMove.METHOD_NAME; - - /** - * Constructs a new MoveRequest and initializes its member variables. - * - * @param session with the access_token - * @param client to make Http requests on - * @param path of the request - * @param entity body of the request - */ - public MoveRequest(LiveConnectSession session, - HttpClient client, - String path, - HttpEntity entity) { - super(session, client, JsonResponseHandler.INSTANCE, path, entity); - } - - /** @return the string "MOVE" */ - @Override - public String getMethod() { - return METHOD; - } - - /** - * Factory method override that constructs a HttpMove and adds a body to it. - * - * @return a HttpMove with the properly body added to it. - */ - @Override - protected HttpUriRequest createHttpRequest() throws LiveOperationException { - final HttpMove request = new HttpMove(this.requestUri.toString()); - - request.setEntity(this.entity); - - return request; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuth.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuth.java deleted file mode 100644 index da6bc452..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuth.java +++ /dev/null @@ -1,214 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * OAuth is a non-instantiable utility class that contains types and constants - * for the OAuth protocol. - * - * See the OAuth 2.0 spec - * for more information. - */ -final class OAuth { - - public enum DisplayType { - ANDROID_PHONE, - ANDROID_TABLET - } - - public enum ErrorType { - /** - * Client authentication failed (e.g. unknown client, no - * client authentication included, or unsupported - * authentication method). The authorization server MAY - * return an HTTP 401 (Unauthorized) status code to indicate - * which HTTP authentication schemes are supported. If the - * client attempted to authenticate via the "Authorization" - * request header field, the authorization server MUST - * respond with an HTTP 401 (Unauthorized) status code, and - * include the "WWW-Authenticate" response header field - * matching the authentication scheme used by the client. - */ - INVALID_CLIENT, - - /** - * The provided authorization grant (e.g. authorization - * code, resource owner credentials, client credentials) is - * invalid, expired, revoked, does not match the redirection - * URI used in the authorization request, or was issued to - * another client. - */ - INVALID_GRANT, - - /** - * The request is missing a required parameter, includes an - * unsupported parameter value, repeats a parameter, - * includes multiple credentials, utilizes more than one - * mechanism for authenticating the client, or is otherwise - * malformed. - */ - INVALID_REQUEST, - - /** - * The requested scope is invalid, unknown, malformed, or - * exceeds the scope granted by the resource owner. - */ - INVALID_SCOPE, - - /** - * The authenticated client is not authorized to use this - * authorization grant type. - */ - UNAUTHORIZED_CLIENT, - - /** - * The authorization grant type is not supported by the - * authorization server. - */ - UNSUPPORTED_GRANT_TYPE; - } - - public enum GrantType { - AUTHORIZATION_CODE, - CLIENT_CREDENTIALS, - PASSWORD, - REFRESH_TOKEN; - } - - public enum ResponseType { - CODE, - TOKEN; - } - - public enum TokenType { - BEARER - } - - /** - * Key for the access_token parameter. - * - * See Section 5.1 - * of the OAuth 2.0 spec for more information. - */ - public static final String ACCESS_TOKEN = "access_token"; - - /** The app's authentication token. */ - public static final String AUTHENTICATION_TOKEN = "authentication_token"; - - /** The app's client ID. */ - public static final String CLIENT_ID = "client_id"; - - /** Equivalent to the profile that is described in the OAuth 2.0 protocol spec. */ - public static final String CODE = "code"; - - /** - * The display type to be used for the authorization page. Valid values are - * "popup", "touch", "page", or "none". - */ - public static final String DISPLAY = "display"; - - /** - * Key for the error parameter. - * - * error can have the following values: - * invalid_request, unauthorized_client, access_denied, unsupported_response_type, - * invalid_scope, server_error, or temporarily_unavailable. - */ - public static final String ERROR = "error"; - - /** - * Key for the error_description parameter. error_description is described below. - * - * OPTIONAL. A human-readable UTF-8 encoded text providing - * additional information, used to assist the client developer in - * understanding the error that occurred. - */ - public static final String ERROR_DESCRIPTION = "error_description"; - - /** - * Key for the error_uri parameter. error_uri is described below. - * - * OPTIONAL. A URI identifying a human-readable web page with - * information about the error, used to provide the client - * developer with additional information about the error. - */ - public static final String ERROR_URI = "error_uri"; - - /** - * Key for the expires_in parameter. expires_in is described below. - * - * OPTIONAL. The lifetime in seconds of the access token. For - * example, the value "3600" denotes that the access token will - * expire in one hour from the time the response was generated. - */ - public static final String EXPIRES_IN = "expires_in"; - - /** - * Key for the grant_type parameter. grant_type is described below. - * - * grant_type is used in a token request. It can take on the following - * values: authorization_code, password, client_credentials, or refresh_token. - */ - public static final String GRANT_TYPE = "grant_type"; - - /** - * Optional. A market string that determines how the consent user interface - * (UI) is localized. If the value of this parameter is missing or is not - * valid, a market value is determined by using an internal algorithm. - */ - public static final String LOCALE = "locale"; - - /** - * Key for the redirect_uri parameter. - * - * See Section 3.1.2 - * of the OAuth 2.0 spec for more information. - */ - public static final String REDIRECT_URI = "redirect_uri"; - - /** - * Key used for the refresh_token parameter. - * - * See Section 5.1 - * of the OAuth 2.0 spec for more information. - */ - public static final String REFRESH_TOKEN = "refresh_token"; - - /** - * The type of data to be returned in the response from the authorization - * server. Valid values are "code" or "token". - */ - public static final String RESPONSE_TYPE = "response_type"; - - /** - * Equivalent to the scope parameter that is described in the OAuth 2.0 - * protocol spec. - */ - public static final String SCOPE = "scope"; - - /** Delimiter for the scopes field response. */ - public static final String SCOPE_DELIMITER = " "; - - /** - * Equivalent to the state parameter that is described in the OAuth 2.0 - * protocol spec. - */ - public static final String STATE = "state"; - - public static final String THEME = "theme"; - - /** - * Key used for the token_type parameter. - * - * See Section 5.1 - * of the OAuth 2.0 spec for more information. - */ - public static final String TOKEN_TYPE = "token_type"; - - /** Private to prevent instantiation */ - private OAuth() { throw new AssertionError(ErrorMessages.NON_INSTANTIABLE_CLASS); } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthErrorResponse.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthErrorResponse.java deleted file mode 100644 index e2f96af3..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthErrorResponse.java +++ /dev/null @@ -1,173 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import org.json.JSONException; -import org.json.JSONObject; - -import com.microsoft.live.OAuth.ErrorType; - -/** - * OAuthErrorResponse represents the an Error Response from the OAuth server. - */ -class OAuthErrorResponse implements OAuthResponse { - - /** - * Builder is a helper class to create a OAuthErrorResponse. - * An OAuthResponse must contain an error, but an error_description and - * error_uri are optional - */ - public static class Builder { - private final ErrorType error; - private String errorDescription; - private String errorUri; - - public Builder(ErrorType error) { - assert error != null; - - this.error = error; - } - - /** - * @return a new instance of an OAuthErrorResponse containing - * the values called on the builder. - */ - public OAuthErrorResponse build() { - return new OAuthErrorResponse(this); - } - - public Builder errorDescription(String errorDescription) { - this.errorDescription = errorDescription; - return this; - } - - public Builder errorUri(String errorUri) { - this.errorUri = errorUri; - return this; - } - } - - /** - * Static constructor that creates an OAuthErrorResponse from the given OAuth server's - * JSONObject response - * @param response from the OAuth server - * @return A new instance of an OAuthErrorResponse from the given response - * @throws LiveAuthException if there is an JSONException, or the error type cannot be found. - */ - public static OAuthErrorResponse createFromJson(JSONObject response) throws LiveAuthException { - final String errorString; - try { - errorString = response.getString(OAuth.ERROR); - } catch (JSONException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - final ErrorType error; - try { - error = ErrorType.valueOf(errorString.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } catch (NullPointerException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - final Builder builder = new Builder(error); - if (response.has(OAuth.ERROR_DESCRIPTION)) { - final String errorDescription; - try { - errorDescription = response.getString(OAuth.ERROR_DESCRIPTION); - } catch (JSONException e) { - throw new LiveAuthException(ErrorMessages.CLIENT_ERROR, e); - } - builder.errorDescription(errorDescription); - } - - if (response.has(OAuth.ERROR_URI)) { - final String errorUri; - try { - errorUri = response.getString(OAuth.ERROR_URI); - } catch (JSONException e) { - throw new LiveAuthException(ErrorMessages.CLIENT_ERROR, e); - } - builder.errorUri(errorUri); - } - - return builder.build(); - } - - /** - * @param response to check - * @return true if the given JSONObject is a valid OAuth response - */ - public static boolean validOAuthErrorResponse(JSONObject response) { - return response.has(OAuth.ERROR); - } - - /** REQUIRED. */ - private final ErrorType error; - - /** - * OPTIONAL. A human-readable UTF-8 encoded text providing - * additional information, used to assist the client developer in - * understanding the error that occurred. - */ - private final String errorDescription; - - /** - * OPTIONAL. A URI identifying a human-readable web page with - * information about the error, used to provide the client - * developer with additional information about the error. - */ - private final String errorUri; - - /** - * OAuthErrorResponse constructor. It is private to enforce - * the use of the Builder. - * - * @param builder to use to construct the object. - */ - private OAuthErrorResponse(Builder builder) { - this.error = builder.error; - this.errorDescription = builder.errorDescription; - this.errorUri = builder.errorUri; - } - - @Override - public void accept(OAuthResponseVisitor visitor) { - visitor.visit(this); - } - - /** - * error is a required field. - * @return the error - */ - public ErrorType getError() { - return error; - } - - /** - * error_description is an optional field - * @return error_description - */ - public String getErrorDescription() { - return errorDescription; - } - - /** - * error_uri is an optional field - * @return error_uri - */ - public String getErrorUri() { - return errorUri; - } - - @Override - public String toString() { - return String.format("OAuthErrorResponse [error=%s, errorDescription=%s, errorUri=%s]", - error.toString().toLowerCase(), errorDescription, errorUri); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthRequestObserver.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthRequestObserver.java deleted file mode 100644 index 7c5e4f01..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthRequestObserver.java +++ /dev/null @@ -1,26 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * An observer of an OAuth Request. It will be notified of an Exception or of a Response. - */ -interface OAuthRequestObserver { - /** - * Callback used on an exception. - * - * @param exception - */ - public void onException(LiveAuthException exception); - - /** - * Callback used on a response. - * - * @param response - */ - public void onResponse(OAuthResponse response); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthResponse.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthResponse.java deleted file mode 100644 index 32ee510a..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthResponse.java +++ /dev/null @@ -1,25 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * OAuthRespresent a response from an OAuth server. - * Known implementors are OAuthSuccessfulResponse and OAuthErrorResponse. - * Different OAuthResponses can be determined by using the OAuthResponseVisitor. - */ -interface OAuthResponse { - - /** - * Calls visit() on the visitor. - * This method is used to determine which OAuthResponse is being returned - * without using instance of. - * - * @param visitor to visit the given OAuthResponse - */ - public void accept(OAuthResponseVisitor visitor); - -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthResponseVisitor.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthResponseVisitor.java deleted file mode 100644 index 8a1b03dd..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthResponseVisitor.java +++ /dev/null @@ -1,27 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * OAuthResponseVisitor is used to visit various OAuthResponse. - */ -interface OAuthResponseVisitor { - - /** - * Called when an OAuthSuccessfulResponse is visited. - * - * @param response being visited - */ - public void visit(OAuthSuccessfulResponse response); - - /** - * Called when an OAuthErrorResponse is being visited. - * - * @param response being visited - */ - public void visit(OAuthErrorResponse response); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthSuccessfulResponse.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthSuccessfulResponse.java deleted file mode 100644 index 5f203a5d..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OAuthSuccessfulResponse.java +++ /dev/null @@ -1,302 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.util.Map; - -import org.json.JSONException; -import org.json.JSONObject; - -import android.text.TextUtils; - -import com.microsoft.live.OAuth.TokenType; - -/** - * OAuthSuccessfulResponse represents a successful response form an OAuth server. - */ -class OAuthSuccessfulResponse implements OAuthResponse { - - /** - * Builder is a utility class that is used to build a new OAuthSuccessfulResponse. - * It must be constructed with the required fields, and can add on the optional ones. - */ - public static class Builder { - private final String accessToken; - private String authenticationToken; - private int expiresIn = UNINITIALIZED; - private String refreshToken; - private String scope; - private final TokenType tokenType; - - public Builder(String accessToken, TokenType tokenType) { - assert accessToken != null; - assert !TextUtils.isEmpty(accessToken); - assert tokenType != null; - - this.accessToken = accessToken; - this.tokenType = tokenType; - } - - public Builder authenticationToken(String authenticationToken) { - this.authenticationToken = authenticationToken; - return this; - } - - /** - * @return a new instance of an OAuthSuccessfulResponse with the given - * parameters passed into the builder. - */ - public OAuthSuccessfulResponse build() { - return new OAuthSuccessfulResponse(this); - } - - public Builder expiresIn(int expiresIn) { - this.expiresIn = expiresIn; - return this; - } - - public Builder refreshToken(String refreshToken) { - this.refreshToken = refreshToken; - return this; - } - - public Builder scope(String scope) { - this.scope = scope; - return this; - } - } - - /** Used to declare expiresIn uninitialized */ - private static final int UNINITIALIZED = -1; - - public static OAuthSuccessfulResponse createFromFragment( - Map fragmentParameters) throws LiveAuthException { - String accessToken = fragmentParameters.get(OAuth.ACCESS_TOKEN); - String tokenTypeString = fragmentParameters.get(OAuth.TOKEN_TYPE); - - // must have accessToken and tokenTypeString to be a valid OAuthSuccessfulResponse - assert accessToken != null; - assert tokenTypeString != null; - - TokenType tokenType; - try { - tokenType = TokenType.valueOf(tokenTypeString.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - OAuthSuccessfulResponse.Builder builder = - new OAuthSuccessfulResponse.Builder(accessToken, tokenType); - - String authenticationToken = fragmentParameters.get(OAuth.AUTHENTICATION_TOKEN); - if (authenticationToken != null) { - builder.authenticationToken(authenticationToken); - } - - String expiresInString = fragmentParameters.get(OAuth.EXPIRES_IN); - if (expiresInString != null) { - final int expiresIn; - try { - expiresIn = Integer.parseInt(expiresInString); - } catch (final NumberFormatException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - builder.expiresIn(expiresIn); - } - - String scope = fragmentParameters.get(OAuth.SCOPE); - if (scope != null) { - builder.scope(scope); - } - - return builder.build(); - } - - /** - * Static constructor used to create a new OAuthSuccessfulResponse from an - * OAuth server's JSON response. - * - * @param response from an OAuth server that is used to create the object. - * @return a new instance of OAuthSuccessfulResponse that is created from the given JSONObject - * @throws LiveAuthException if there is a JSONException or the token_type is unknown. - */ - public static OAuthSuccessfulResponse createFromJson(JSONObject response) - throws LiveAuthException { - assert validOAuthSuccessfulResponse(response); - - final String accessToken; - try { - accessToken = response.getString(OAuth.ACCESS_TOKEN); - } catch (final JSONException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - final String tokenTypeString; - try { - tokenTypeString = response.getString(OAuth.TOKEN_TYPE); - } catch (final JSONException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - final TokenType tokenType; - try { - tokenType = TokenType.valueOf(tokenTypeString.toUpperCase()); - } catch (final IllegalArgumentException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } catch (final NullPointerException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - final Builder builder = new Builder(accessToken, tokenType); - - if (response.has(OAuth.AUTHENTICATION_TOKEN)) { - final String authenticationToken; - try { - authenticationToken = response.getString(OAuth.AUTHENTICATION_TOKEN); - } catch (final JSONException e) { - throw new LiveAuthException(ErrorMessages.CLIENT_ERROR, e); - } - builder.authenticationToken(authenticationToken); - } - - if (response.has(OAuth.REFRESH_TOKEN)) { - final String refreshToken; - try { - refreshToken = response.getString(OAuth.REFRESH_TOKEN); - } catch (final JSONException e) { - throw new LiveAuthException(ErrorMessages.CLIENT_ERROR, e); - } - builder.refreshToken(refreshToken); - } - - if (response.has(OAuth.EXPIRES_IN)) { - final int expiresIn; - try { - expiresIn = response.getInt(OAuth.EXPIRES_IN); - } catch (final JSONException e) { - throw new LiveAuthException(ErrorMessages.CLIENT_ERROR, e); - } - builder.expiresIn(expiresIn); - } - - if (response.has(OAuth.SCOPE)) { - final String scope; - try { - scope = response.getString(OAuth.SCOPE); - } catch (final JSONException e) { - throw new LiveAuthException(ErrorMessages.CLIENT_ERROR, e); - } - builder.scope(scope); - } - - return builder.build(); - } - - /** - * @param response - * @return true if the given JSONObject has the required fields to construct an - * OAuthSuccessfulResponse (i.e., has access_token and token_type) - */ - public static boolean validOAuthSuccessfulResponse(JSONObject response) { - return response.has(OAuth.ACCESS_TOKEN) && - response.has(OAuth.TOKEN_TYPE); - } - - /** REQUIRED. The access token issued by the authorization server. */ - private final String accessToken; - - private final String authenticationToken; - - /** - * OPTIONAL. The lifetime in seconds of the access token. For - * example, the value "3600" denotes that the access token will - * expire in one hour from the time the response was generated. - */ - private final int expiresIn; - - /** - * OPTIONAL. The refresh token which can be used to obtain new - * access tokens using the same authorization grant. - */ - private final String refreshToken; - - /** OPTIONAL. */ - private final String scope; - - /** REQUIRED. */ - private final TokenType tokenType; - - /** - * Private constructor to enforce the user of the builder. - * @param builder to use to construct the object from. - */ - private OAuthSuccessfulResponse(Builder builder) { - this.accessToken = builder.accessToken; - this.authenticationToken = builder.authenticationToken; - this.tokenType = builder.tokenType; - this.refreshToken = builder.refreshToken; - this.expiresIn = builder.expiresIn; - this.scope = builder.scope; - } - - @Override - public void accept(OAuthResponseVisitor visitor) { - visitor.visit(this); - } - - public String getAccessToken() { - return this.accessToken; - } - - public String getAuthenticationToken() { - return this.authenticationToken; - } - - public int getExpiresIn() { - return this.expiresIn; - } - - public String getRefreshToken() { - return this.refreshToken; - } - - public String getScope() { - return this.scope; - } - - public TokenType getTokenType() { - return this.tokenType; - } - - public boolean hasAuthenticationToken() { - return this.authenticationToken != null && !TextUtils.isEmpty(this.authenticationToken); - } - - public boolean hasExpiresIn() { - return this.expiresIn != UNINITIALIZED; - } - - public boolean hasRefreshToken() { - return this.refreshToken != null && !TextUtils.isEmpty(this.refreshToken); - } - - public boolean hasScope() { - return this.scope != null && !TextUtils.isEmpty(this.scope); - } - - @Override - public String toString() { - return String.format("OAuthSuccessfulResponse [accessToken=%s, authenticationToken=%s, tokenType=%s, refreshToken=%s, expiresIn=%s, scope=%s]", - this.accessToken, - this.authenticationToken, - this.tokenType, - this.refreshToken, - this.expiresIn, - this.scope); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ObservableOAuthRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ObservableOAuthRequest.java deleted file mode 100644 index a4422408..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ObservableOAuthRequest.java +++ /dev/null @@ -1,28 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * An OAuth Request that can be observed, by adding observers that will be notified on any - * exception or response. - */ -interface ObservableOAuthRequest { - /** - * Adds an observer to observe the OAuth request - * - * @param observer to add - */ - public void addObserver(OAuthRequestObserver observer); - - /** - * Removes an observer that is observing the OAuth request - * - * @param observer to remove - * @return true if the observer was removed. - */ - public boolean removeObserver(OAuthRequestObserver observer); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OverwriteOption.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OverwriteOption.java deleted file mode 100644 index d261ecd1..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/OverwriteOption.java +++ /dev/null @@ -1,47 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * Enum that specifies what to do during a naming conflict during an upload. - */ -public enum OverwriteOption { - - /** Overwrite the existing file. */ - Overwrite { - @Override - protected String overwriteQueryParamValue() { - return "true"; - } - }, - - /** Do Not Overwrite the existing file and cancel the upload. */ - DoNotOverwrite { - @Override - protected String overwriteQueryParamValue() { - return "false"; - } - }, - - /** Rename the current file to avoid a name conflict. */ - Rename { - @Override - protected String overwriteQueryParamValue() { - return "choosenewname"; - } - }; - - /** - * Leaves any existing overwrite query parameter on appends this overwrite - * to the given UriBuilder. - */ - void appendQueryParameterOnTo(UriBuilder uri) { - uri.appendQueryParameter(QueryParameters.OVERWRITE, this.overwriteQueryParamValue()); - } - - abstract protected String overwriteQueryParamValue(); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/PostRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/PostRequest.java deleted file mode 100644 index 3a995750..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/PostRequest.java +++ /dev/null @@ -1,56 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import org.apache.http.HttpEntity; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; -import org.json.JSONObject; - -/** - * PostRequest is a subclass of a BodyEnclosingApiRequest and performs a Post request. - */ -class PostRequest extends EntityEnclosingApiRequest { - - public static final String METHOD = HttpPost.METHOD_NAME; - - /** - * Constructs a new PostRequest and initializes its member variables. - * - * @param session with the access_token - * @param client to make Http requests on - * @param path of the request - * @param entity body of the request - */ - public PostRequest(LiveConnectSession session, - HttpClient client, - String path, - HttpEntity entity) { - super(session, client, JsonResponseHandler.INSTANCE, path, entity); - } - - /** @return the string "POST" */ - @Override - public String getMethod() { - return METHOD; - } - - /** - * Factory method override that constructs a HttpPost and adds a body to it. - * - * @return a HttpPost with the properly body added to it. - */ - @Override - protected HttpUriRequest createHttpRequest() throws LiveOperationException { - final HttpPost request = new HttpPost(this.requestUri.toString()); - - request.setEntity(this.entity); - - return request; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/PreferencesConstants.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/PreferencesConstants.java deleted file mode 100644 index 78a2b536..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/PreferencesConstants.java +++ /dev/null @@ -1,22 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * Static class that holds constants used by an application's preferences. - */ -final class PreferencesConstants { - public static final String COOKIES_KEY = "cookies"; - - /** Name of the preference file */ - public static final String FILE_NAME = "com.microsoft.live"; - - public static final String REFRESH_TOKEN_KEY = "refresh_token"; - public static final String COOKIE_DELIMITER = ","; - - private PreferencesConstants() { throw new AssertionError(); } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/PutRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/PutRequest.java deleted file mode 100644 index 32de9257..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/PutRequest.java +++ /dev/null @@ -1,56 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import org.apache.http.HttpEntity; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpUriRequest; -import org.json.JSONObject; - -/** - * PutRequest is a subclass of a BodyEnclosingApiRequest and performs a Put request. - */ -class PutRequest extends EntityEnclosingApiRequest { - - public static final String METHOD = HttpPut.METHOD_NAME; - - /** - * Constructs a new PutRequest and initializes its member variables. - * - * @param session with the access_token - * @param client to make Http requests on - * @param path of the request - * @param entity body of the request - */ - public PutRequest(LiveConnectSession session, - HttpClient client, - String path, - HttpEntity entity) { - super(session, client, JsonResponseHandler.INSTANCE, path, entity); - } - - /** @return the string "PUT" */ - @Override - public String getMethod() { - return METHOD; - } - - /** - * Factory method override that constructs a HttpPut and adds a body to it. - * - * @return a HttpPut with the properly body added to it. - */ - @Override - protected HttpUriRequest createHttpRequest() throws LiveOperationException { - final HttpPut request = new HttpPut(this.requestUri.toString()); - - request.setEntity(this.entity); - - return request; - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/QueryParameters.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/QueryParameters.java deleted file mode 100644 index 6200bfce..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/QueryParameters.java +++ /dev/null @@ -1,27 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -/** - * QueryParameters is a non-instantiable utility class that holds query parameter constants - * used by the API service. - */ -final class QueryParameters { - - public static final String PRETTY = "pretty"; - public static final String CALLBACK = "callback"; - public static final String SUPPRESS_REDIRECTS = "suppress_redirects"; - public static final String SUPPRESS_RESPONSE_CODES = "suppress_response_codes"; - public static final String METHOD = "method"; - public static final String OVERWRITE = "overwrite"; - public static final String RETURN_SSL_RESOURCES = "return_ssl_resources"; - - /** Private to present instantiation. */ - private QueryParameters() { - throw new AssertionError(ErrorMessages.NON_INSTANTIABLE_CLASS); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/RefreshAccessTokenRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/RefreshAccessTokenRequest.java deleted file mode 100644 index c836d247..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/RefreshAccessTokenRequest.java +++ /dev/null @@ -1,55 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.util.List; - -import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; -import org.apache.http.message.BasicNameValuePair; - -import android.text.TextUtils; - -import com.microsoft.live.OAuth.GrantType; - -/** - * RefreshAccessTokenRequest performs a refresh access token request. Most of the work - * is done by the parent class, TokenRequest. This class adds in the required body parameters via - * TokenRequest's hook method, constructBody(). - */ -class RefreshAccessTokenRequest extends TokenRequest { - - /** REQUIRED. Value MUST be set to "refresh_token". */ - private final GrantType grantType = GrantType.REFRESH_TOKEN; - - /** REQUIRED. The refresh token issued to the client. */ - private final String refreshToken; - - private final String scope; - - public RefreshAccessTokenRequest(HttpClient client, - String clientId, - String refreshToken, - String scope) { - super(client, clientId); - - assert refreshToken != null; - assert !TextUtils.isEmpty(refreshToken); - assert scope != null; - assert !TextUtils.isEmpty(scope); - - this.refreshToken = refreshToken; - this.scope = scope; - } - - @Override - protected void constructBody(List body) { - body.add(new BasicNameValuePair(OAuth.REFRESH_TOKEN, this.refreshToken)); - body.add(new BasicNameValuePair(OAuth.SCOPE, this.scope)); - body.add(new BasicNameValuePair(OAuth.GRANT_TYPE, this.grantType.toString())); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ScreenSize.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ScreenSize.java deleted file mode 100644 index ec67f36b..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/ScreenSize.java +++ /dev/null @@ -1,73 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import android.app.Activity; -import android.content.res.Configuration; -import android.util.Log; - -/** - * The ScreenSize is used to determine the DeviceType. - * Small and Normal ScreenSizes are Phones. - * Large and XLarge are Tablets. - */ -enum ScreenSize { - SMALL { - @Override - public DeviceType getDeviceType() { - return DeviceType.PHONE; - } - }, - NORMAL { - @Override - public DeviceType getDeviceType() { - return DeviceType.PHONE; - } - - }, - LARGE { - @Override - public DeviceType getDeviceType() { - return DeviceType.TABLET; - } - }, - XLARGE { - @Override - public DeviceType getDeviceType() { - return DeviceType.TABLET; - } - }; - - public abstract DeviceType getDeviceType(); - - /** - * Configuration.SCREENLAYOUT_SIZE_XLARGE was not provided in API level 9. - * However, its value of 4 does show up. - */ - private static final int SCREENLAYOUT_SIZE_XLARGE = 4; - - public static ScreenSize determineScreenSize(Activity activity) { - int screenLayout = activity.getResources().getConfiguration().screenLayout; - int screenLayoutMasked = screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK; - switch (screenLayoutMasked) { - case Configuration.SCREENLAYOUT_SIZE_SMALL: - return SMALL; - case Configuration.SCREENLAYOUT_SIZE_NORMAL: - return NORMAL; - case Configuration.SCREENLAYOUT_SIZE_LARGE: - return LARGE; - case SCREENLAYOUT_SIZE_XLARGE: - return XLARGE; - default: - // If we cannot determine the ScreenSize, we'll guess and say it's normal. - Log.d( - "Live SDK ScreenSize", - "Unable to determine ScreenSize. A Normal ScreenSize will be returned."); - return NORMAL; - } - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/TokenRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/TokenRequest.java deleted file mode 100644 index 1b0d56f0..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/TokenRequest.java +++ /dev/null @@ -1,125 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.protocol.HTTP; -import org.apache.http.util.EntityUtils; -import org.json.JSONException; -import org.json.JSONObject; - -import android.net.Uri; -import android.text.TextUtils; - -/** - * Abstract class that represents an OAuth token request. - * Known subclasses include AccessTokenRequest and RefreshAccessTokenRequest - */ -abstract class TokenRequest { - - private static final String CONTENT_TYPE = - URLEncodedUtils.CONTENT_TYPE + ";charset=" + HTTP.UTF_8; - - protected final HttpClient client; - protected final String clientId; - - /** - * Constructs a new TokenRequest instance and initializes its parameters. - * - * @param client the HttpClient to make HTTP requests on - * @param clientId the client_id of the calling application - */ - public TokenRequest(HttpClient client, String clientId) { - assert client != null; - assert clientId != null; - assert !TextUtils.isEmpty(clientId); - - this.client = client; - this.clientId = clientId; - } - - /** - * Performs the Token Request and returns the OAuth server's response. - * - * @return The OAuthResponse from the server - * @throws LiveAuthException if there is any exception while executing the request - * (e.g., IOException, JSONException) - */ - public OAuthResponse execute() throws LiveAuthException { - final Uri requestUri = Config.INSTANCE.getOAuthTokenUri(); - - final HttpPost request = new HttpPost(requestUri.toString()); - - final List body = new ArrayList(); - body.add(new BasicNameValuePair(OAuth.CLIENT_ID, this.clientId)); - - // constructBody allows subclasses to add to body - this.constructBody(body); - - try { - final UrlEncodedFormEntity entity = new UrlEncodedFormEntity(body, HTTP.UTF_8); - entity.setContentType(CONTENT_TYPE); - request.setEntity(entity); - } catch (UnsupportedEncodingException e) { - throw new LiveAuthException(ErrorMessages.CLIENT_ERROR, e); - } - - final HttpResponse response; - try { - response = this.client.execute(request); - } catch (ClientProtocolException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } catch (IOException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - final HttpEntity entity = response.getEntity(); - final String stringResponse; - try { - stringResponse = EntityUtils.toString(entity); - } catch (IOException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - final JSONObject jsonResponse; - try { - jsonResponse = new JSONObject(stringResponse); - } catch (JSONException e) { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR, e); - } - - if (OAuthErrorResponse.validOAuthErrorResponse(jsonResponse)) { - return OAuthErrorResponse.createFromJson(jsonResponse); - } else if (OAuthSuccessfulResponse.validOAuthSuccessfulResponse(jsonResponse)) { - return OAuthSuccessfulResponse.createFromJson(jsonResponse); - } else { - throw new LiveAuthException(ErrorMessages.SERVER_ERROR); - } - } - - /** - * This method gives a hook in the execute process, and allows subclasses - * to add to the HttpRequest's body. - * NOTE: The content type has already been added - * - * @param body of NameValuePairs to add to - */ - protected abstract void constructBody(List body); -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/TokenRequestAsync.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/TokenRequestAsync.java deleted file mode 100644 index f6db6c44..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/TokenRequestAsync.java +++ /dev/null @@ -1,79 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import android.os.AsyncTask; - -/** - * TokenRequestAsync performs an async token request. It takes in a TokenRequest, - * executes it, checks the OAuthResponse, and then calls the given listener. - */ -class TokenRequestAsync extends AsyncTask implements ObservableOAuthRequest { - - private final DefaultObservableOAuthRequest observerable; - - /** Not null if there was an exception */ - private LiveAuthException exception; - - /** Not null if there was a response */ - private OAuthResponse response; - - private final TokenRequest request; - - /** - * Constructs a new TokenRequestAsync and initializes its member variables - * - * @param request to perform - */ - public TokenRequestAsync(TokenRequest request) { - assert request != null; - - this.observerable = new DefaultObservableOAuthRequest(); - this.request = request; - } - - @Override - public void addObserver(OAuthRequestObserver observer) { - this.observerable.addObserver(observer); - } - - @Override - public boolean removeObserver(OAuthRequestObserver observer) { - return this.observerable.removeObserver(observer); - } - - @Override - protected Void doInBackground(Void... params) { - try { - this.response = this.request.execute(); - } catch (LiveAuthException e) { - this.exception = e; - } - - return null; - } - - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - - if (this.response != null) { - this.observerable.notifyObservers(this.response); - } else if (this.exception != null) { - this.observerable.notifyObservers(this.exception); - } else { - final LiveAuthException exception = new LiveAuthException(ErrorMessages.CLIENT_ERROR); - this.observerable.notifyObservers(exception); - } - } - - public void executeSynchronous() { - Void result = doInBackground(); - onPostExecute(result); - - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/UploadRequest.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/UploadRequest.java deleted file mode 100644 index 489c2e43..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/UploadRequest.java +++ /dev/null @@ -1,137 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import org.apache.http.HttpEntity; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpUriRequest; -import org.json.JSONException; -import org.json.JSONObject; - -import android.net.Uri; -import android.text.TextUtils; - -class UploadRequest extends EntityEnclosingApiRequest { - - public static final String METHOD = HttpPut.METHOD_NAME; - - private static final String FILE_PATH = "file."; - private static final String ERROR_KEY = "error"; - private static final String UPLOAD_LOCATION_KEY = "upload_location"; - - private HttpUriRequest currentRequest; - private final String filename; - - /** - * true if the given path refers to a File Object - * (i.e., the path begins with "/file"). - */ - private final boolean isFileUpload; - - private final OverwriteOption overwrite; - - public UploadRequest(LiveConnectSession session, - HttpClient client, - String path, - HttpEntity entity, - String filename, - OverwriteOption overwrite) { - super(session, - client, - JsonResponseHandler.INSTANCE, - path, - entity, - ResponseCodes.SUPPRESS, - Redirects.UNSUPPRESSED); - - assert !TextUtils.isEmpty(filename); - - this.filename = filename; - this.overwrite = overwrite; - - String lowerCasePath = this.pathUri.getPath().toLowerCase(); - this.isFileUpload = lowerCasePath.indexOf(FILE_PATH) != -1; - } - - @Override - public String getMethod() { - return METHOD; - } - - @Override - public JSONObject execute() throws LiveOperationException { - UriBuilder uploadRequestUri; - - // if the path was relative, we have to retrieve the upload location, because if we don't, - // we will proxy the upload request, which is a waste of resources. - if (this.pathUri.isRelative()) { - JSONObject response = this.getUploadLocation(); - - // We could of tried to get the upload location on an invalid path. - // If we did, just return that response. - // If the user passes in a path that does contain an upload location, then - // we need to throw an error. - if (response.has(ERROR_KEY)) { - return response; - } else if (!response.has(UPLOAD_LOCATION_KEY)) { - throw new LiveOperationException(ErrorMessages.MISSING_UPLOAD_LOCATION); - } - - // once we have the file object, get the upload location - String uploadLocation; - try { - uploadLocation = response.getString(UPLOAD_LOCATION_KEY); - } catch (JSONException e) { - throw new LiveOperationException(ErrorMessages.SERVER_ERROR, e); - } - - uploadRequestUri = UriBuilder.newInstance(Uri.parse(uploadLocation)); - - // The original path might have query parameters that were sent to the - // the upload location request, and those same query parameters will need - // to be sent to the HttpPut upload request too. Also, the returned upload_location - // *could* have query parameters on it. We want to keep those intact and in front of the - // the client's query parameters. - uploadRequestUri.appendQueryString(this.pathUri.getQuery()); - } else { - uploadRequestUri = this.requestUri; - } - - if (!this.isFileUpload) { - // if it is not a file upload it is a folder upload and we must - // add the file name to the upload location - // and don't forget to set the overwrite query parameter - uploadRequestUri.appendToPath(this.filename); - this.overwrite.appendQueryParameterOnTo(uploadRequestUri); - } - - HttpPut uploadRequest = new HttpPut(uploadRequestUri.toString()); - uploadRequest.setEntity(this.entity); - - this.currentRequest = uploadRequest; - - return super.execute(); - } - - @Override - protected HttpUriRequest createHttpRequest() throws LiveOperationException { - return this.currentRequest; - } - - /** - * Performs an HttpGet on the folder/file object to retrieve the upload_location - * - * @return - * @throws LiveOperationException if there was an error getting the getUploadLocation - */ - private JSONObject getUploadLocation() throws LiveOperationException { - this.currentRequest = new HttpGet(this.requestUri.toString()); - return super.execute(); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/UriBuilder.java b/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/UriBuilder.java deleted file mode 100644 index 9dc499ca..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/com/microsoft/live/UriBuilder.java +++ /dev/null @@ -1,277 +0,0 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2012 Microsoft Corporation. All rights reserved. -// -// Description: See the class level JavaDoc comments. -//------------------------------------------------------------------------------ - -package com.microsoft.live; - -import java.util.Iterator; -import java.util.LinkedList; - -import android.net.Uri; -import android.text.TextUtils; -import android.util.Log; - -/** - * Class for building URIs. The most useful benefit of this class is its query parameter - * management. It stores all the query parameters in a LinkedList, so parameters can - * be looked up, removed, and added easily. - */ -class UriBuilder { - - public static class QueryParameter { - private final String key; - private final String value; - - /** - * Constructs a query parameter with no value (e.g., download). - * - * @param key - */ - public QueryParameter(String key) { - assert key != null; - - this.key = key; - this.value = null; - } - - public QueryParameter(String key, String value) { - assert key != null; - assert value != null; - - this.key = key; - this.value = value; - } - - public String getKey() { - return this.key; - } - - public String getValue() { - return this.value; - } - - public boolean hasValue() { - return this.value != null; - } - - @Override - public String toString() { - if (this.hasValue()) { - return this.key + "=" + this.value; - } - - return this.key; - } - } - - private static final String EQUAL = "="; - private static final String AMPERSAND = "&"; - private static final char FORWARD_SLASH = '/'; - - private String scheme; - private String host; - private StringBuilder path; - - private final LinkedList queryParameters; - - /** - * Constructs a new UriBuilder from the given Uri. - * - * @return a new Uri Builder based off the given Uri. - */ - public static UriBuilder newInstance(Uri uri) { - return new UriBuilder().scheme(uri.getScheme()) - .host(uri.getHost()) - .path(uri.getPath()) - .query(uri.getQuery()); - } - - public UriBuilder() { - this.queryParameters = new LinkedList(); - } - - /** - * Appends a new query parameter to the UriBuilder's query string. - * - * (e.g., appendQueryParameter("k1", "v1") when UriBuilder's query string is - * k2=v2&k3=v3 results in k2=v2&k3=v3&k1=v1). - * - * @param key Key of the new query parameter. - * @param value Value of the new query parameter. - * @return this UriBuilder object. Useful for chaining. - */ - public UriBuilder appendQueryParameter(String key, String value) { - assert key != null; - assert value != null; - - this.queryParameters.add(new QueryParameter(key, value)); - - return this; - } - - /** - * Appends the given query string on to the existing UriBuilder's query parameters. - * - * (e.g., UriBuilder's queryString k1=v1&k2=v2 and given queryString k3=v3&k4=v4, results in - * k1=v1&k2=v2&k3=v3&k4=v4). - * - * @param queryString Key-Value pairs separated by & and = (e.g., k1=v1&k2=v2&k3=k3). - * @return this UriBuilder object. Useful for chaining. - */ - public UriBuilder appendQueryString(String queryString) { - if (queryString == null) { - return this; - } - - String[] pairs = TextUtils.split(queryString, UriBuilder.AMPERSAND); - for(String pair : pairs) { - String[] splitPair = TextUtils.split(pair, UriBuilder.EQUAL); - if (splitPair.length == 2) { - String key = splitPair[0]; - String value = splitPair[1]; - - this.queryParameters.add(new QueryParameter(key, value)); - } else if (splitPair.length == 1){ - String key = splitPair[0]; - - this.queryParameters.add(new QueryParameter(key)); - } else { - Log.w("com.microsoft.live.UriBuilder", "Invalid query parameter: " + pair); - } - } - - return this; - } - - /** - * Appends the given path to the UriBuilder's current path. - * - * @param path The path to append onto this UriBuilder's path. - * @return this UriBuilder object. Useful for chaining. - */ - public UriBuilder appendToPath(String path) { - assert path != null; - - if (this.path == null) { - this.path = new StringBuilder(path); - } else { - boolean endsWithSlash = TextUtils.isEmpty(this.path) ? false : - this.path.charAt(this.path.length() - 1) == UriBuilder.FORWARD_SLASH; - boolean pathIsEmpty = TextUtils.isEmpty(path); - boolean beginsWithSlash = - pathIsEmpty ? false : path.charAt(0) == UriBuilder.FORWARD_SLASH; - - if (endsWithSlash && beginsWithSlash) { - if (path.length() > 1) { - this.path.append(path.substring(1)); - - } - } else if (!endsWithSlash && !beginsWithSlash) { - if (!pathIsEmpty) { - this.path.append(UriBuilder.FORWARD_SLASH).append(path); - } - } else { - this.path.append(path); - } - } - - return this; - } - - /** - * Builds the Uri by converting into a android.net.Uri object. - * - * @return a new android.net.Uri defined by what was given to the builder. - */ - public Uri build() { - return new Uri.Builder().scheme(this.scheme) - .authority(this.host) - .path(this.path == null ? "" : this.path.toString()) - .encodedQuery(TextUtils.join("&", this.queryParameters)) - .build(); - } - - /** - * Sets the host part of the Uri. - * - * @return this UriBuilder object. Useful for chaining. - */ - public UriBuilder host(String host) { - assert host != null; - this.host = host; - - return this; - } - - /** - * Sets the path and removes any previously existing path. - * - * @param path The path to set on this UriBuilder. - * @return this UriBuilder object. Useful for chaining. - */ - public UriBuilder path(String path) { - assert path != null; - this.path = new StringBuilder(path); - - return this; - } - - /** - * Takes a query string and puts it in the Uri Builder's query string removing - * any existing query parameters. - * - * @param queryString Key-Value pairs separated by & and = (e.g., k1=v1&k2=v2&k3=k3). - * @return this UriBuilder object. Useful for chaining. - */ - public UriBuilder query(String queryString) { - this.queryParameters.clear(); - - return this.appendQueryString(queryString); - } - - /** - * Removes all query parameters from the UriBuilder that has the given key. - * - * (e.g., removeQueryParametersWithKey("k1") when UriBuilder's query string of k1=v1&k2=v2&k1=v3 - * results in k2=v2). - * - * @param key Query parameter's key to remove - * @return this UriBuilder object. Useful for chaining. - */ - public UriBuilder removeQueryParametersWithKey(String key) { - // There could be multiple query parameters with this key and - // we want to remove all of them. - Iterator it = this.queryParameters.iterator(); - - while (it.hasNext()) { - QueryParameter qp = it.next(); - if (qp.getKey().equals(key)) { - it.remove(); - } - } - - return this; - } - - /** - * Sets the scheme part of the Uri. - * - * @return this UriBuilder object. Useful for chaining. - */ - public UriBuilder scheme(String scheme) { - assert scheme != null; - this.scheme = scheme; - - return this; - } - - /** - * Returns the URI in string format (e.g., http://foo.com/bar?k1=v2). - */ - @Override - public String toString() { - return this.build().toString(); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/DropboxAppFolderFileStorage.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/DropboxAppFolderFileStorage.java deleted file mode 100644 index 2dc5ddd3..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/DropboxAppFolderFileStorage.java +++ /dev/null @@ -1,28 +0,0 @@ -package keepass2android.javafilestorage; - -import com.dropbox.client2.session.Session.AccessType; - -import android.content.Context; - -public class DropboxAppFolderFileStorage extends DropboxFileStorage { - - public DropboxAppFolderFileStorage(Context ctx, String _appKey, - String _appSecret) { - super(ctx, _appKey, _appSecret, false, AccessType.APP_FOLDER); - - - } - - public DropboxAppFolderFileStorage(Context ctx, String _appKey, String _appSecret, boolean clearKeysOnStart) - { - super(ctx, _appKey, _appSecret, clearKeysOnStart, AccessType.APP_FOLDER); - - } - - - @Override - public String getProtocolId() { - return "dropboxKP2A"; - } - -} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/DropboxFileStorage.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/DropboxFileStorage.java deleted file mode 100644 index 7768842f..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/DropboxFileStorage.java +++ /dev/null @@ -1,529 +0,0 @@ -package keepass2android.javafilestorage; - -import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Bundle; -import android.util.Log; -import android.widget.Toast; - -import com.dropbox.client2.DropboxAPI; -import com.dropbox.client2.android.AndroidAuthSession; -import com.dropbox.client2.android.AuthActivity; -import com.dropbox.client2.exception.DropboxException; -import com.dropbox.client2.exception.DropboxServerException; -import com.dropbox.client2.exception.DropboxUnlinkedException; -import com.dropbox.client2.session.AccessTokenPair; -import com.dropbox.client2.session.AppKeyPair; -import com.dropbox.client2.session.TokenPair; -import com.dropbox.client2.session.Session.AccessType; - - - -public class DropboxFileStorage extends JavaFileStorageBase { - - final static private String TAG = "KP2AJ"; - - final static private String ACCOUNT_PREFS_NAME = "prefs"; - final static private String ACCESS_KEY_NAME = "ACCESS_KEY"; - final static private String ACCESS_SECRET_NAME = "ACCESS_SECRET"; - - DropboxAPI mApi; - private boolean mLoggedIn = false; - private Context mContext; - - protected AccessType mAccessType = AccessType.DROPBOX; - - - private String appKey; - private String appSecret; - - public DropboxFileStorage(Context ctx, String _appKey, String _appSecret) - { - initialize(ctx, _appKey, _appSecret, false, mAccessType); - } - - public DropboxFileStorage(Context ctx, String _appKey, String _appSecret, boolean clearKeysOnStart) - { - initialize(ctx, _appKey, _appSecret, clearKeysOnStart, mAccessType); - } - - public DropboxFileStorage(Context ctx, String _appKey, String _appSecret, boolean clearKeysOnStart, AccessType accessType) - { - initialize(ctx, _appKey, _appSecret, clearKeysOnStart, accessType); - } - - private void initialize(Context ctx, String _appKey, String _appSecret, - boolean clearKeysOnStart, AccessType accessType) { - appKey = _appKey; - appSecret = _appSecret; - mContext = ctx; - - if (clearKeysOnStart) - clearKeys(); - - this.mAccessType = accessType; - - // We create a new AuthSession so that we can use the Dropbox API. - AndroidAuthSession session = buildSession(); - mApi = new DropboxAPI(session); - - checkAppKeySetup(); - } - - public boolean tryConnect(Activity activity) - { - if (!mLoggedIn) - mApi.getSession().startAuthentication(activity); - return mLoggedIn; - } - - private void setLoggedIn(boolean b) { - mLoggedIn = b; - - } - - private boolean checkAppKeySetup() { - - // Check if the app has set up its manifest properly. - Intent testIntent = new Intent(Intent.ACTION_VIEW); - String scheme = "db-" + appKey; - String uri = scheme + "://" + AuthActivity.AUTH_VERSION + "/test"; - testIntent.setData(Uri.parse(uri)); - PackageManager pm = mContext.getPackageManager(); - if (0 == pm.queryIntentActivities(testIntent, 0).size()) { - showToast("URL scheme in your app's " + - "manifest is not set up correctly. You should have a " + - "com.dropbox.client2.android.AuthActivity with the " + - "scheme: " + scheme); - return false; - } - return true; - } - - public boolean isConnected() - { - return mLoggedIn; - } - - - public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception - { - if ((previousFileVersion == null) || (previousFileVersion.equals(""))) - return false; - try { - path = removeProtocol(path); - com.dropbox.client2.DropboxAPI.Entry entry = mApi.metadata(path, 1, null, false, null); - return entry.hash != previousFileVersion; - } catch (DropboxException e) { - throw convertException(e); - } - } - - public String getCurrentFileVersionFast(String path) - { - try { - path = removeProtocol(path); - com.dropbox.client2.DropboxAPI.Entry entry = mApi.metadata(path, 1, null, false, null); - return entry.rev; - } catch (DropboxException e) { - Log.d(TAG, e.toString()); - return ""; - } - } - - public InputStream openFileForRead(String path) throws Exception - { - try { - path = removeProtocol(path); - return mApi.getFileStream(path, null); - } catch (DropboxException e) { - //System.out.println("Something went wrong: " + e); - throw convertException(e); - } - } - - public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception - { - ByteArrayInputStream bis = new ByteArrayInputStream(data); - try { - path = removeProtocol(path); - //TODO: it would be nice to be able to use the parent version with putFile() - mApi.putFileOverwrite(path, bis, data.length, null); - } catch (DropboxException e) { - throw convertException(e); - } - } - - private Exception convertException(DropboxException e) { - - Log.d(TAG, "Exception of type " +e.getClass().getName()+":" + e.getMessage()); - - if (DropboxUnlinkedException.class.isAssignableFrom(e.getClass()) ) - { - Log.d(TAG, "LoggedIn=false (due to unlink exception)"); - setLoggedIn(false); - clearKeys(); - return new UserInteractionRequiredException("Unlinked from Dropbox! User must re-link.", e); - - } - - //test for special error FileNotFound which must be reported with FileNotFoundException - if (DropboxServerException.class.isAssignableFrom(e.getClass()) ) - { - - DropboxServerException serverEx = (DropboxServerException)e; - if (serverEx.error == 404) - return new FileNotFoundException(e.toString()); - } - - return e; - } - - private void showToast(String msg) { - Toast error = Toast.makeText(mContext, msg, Toast.LENGTH_LONG); - error.show(); - } - - /** - * Keep the access keys returned from Trusted Authenticator in a local - * store, rather than storing user name & password, and re-authenticating each - * time (which is not to be done, ever). - * - * @return Array of [access_key, access_secret], or null if none stored - */ - private String[] getKeys() { - SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0); - String key = prefs.getString(ACCESS_KEY_NAME, null); - String secret = prefs.getString(ACCESS_SECRET_NAME, null); - if (key != null && secret != null) { - String[] ret = new String[2]; - ret[0] = key; - ret[1] = secret; - return ret; - } else { - return null; - } - } - - /** - * Keeping the access keys returned from Trusted Authenticator in a local - * store, rather than storing user name & password, and re-authenticating each - * time (which is not to be done, ever). - */ - private void storeKeys(String key, String secret) { - Log.d(TAG, "Storing Dropbox accessToken"); - // Save the access key for later - SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0); - Editor edit = prefs.edit(); - edit.putString(ACCESS_KEY_NAME, key); - edit.putString(ACCESS_SECRET_NAME, secret); - edit.commit(); - } - - private void clearKeys() { - SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0); - Editor edit = prefs.edit(); - edit.clear(); - edit.commit(); - } - - private AndroidAuthSession buildSession() { - AppKeyPair appKeyPair = new AppKeyPair(appKey, appSecret); - AndroidAuthSession session; - - String[] stored = getKeys(); - if (stored != null) { - AccessTokenPair accessToken = new AccessTokenPair(stored[0], stored[1]); - session = new AndroidAuthSession(appKeyPair, mAccessType, accessToken); - setLoggedIn(true); - Log.d(TAG, "Creating Dropbox Session with accessToken"); - } else { - session = new AndroidAuthSession(appKeyPair, mAccessType); - setLoggedIn(false); - Log.d(TAG, "Creating Dropbox Session without accessToken"); - } - - return session; - } - - @Override - public String createFolder(String parentPath, String newDirName) throws Exception { - try - { - String path = parentPath; - if (!path.endsWith("/")) - path = path + "/"; - path = path + newDirName; - - String pathWithoutProtocol = removeProtocol(path); - mApi.createFolder(pathWithoutProtocol); - return path; - } - catch (DropboxException e) { - throw convertException(e); - } - } - - @Override - public String createFilePath(String parentPath, String newFileName) throws Exception { - String path = parentPath; - if (!path.endsWith("/")) - path = path + "/"; - path = path + newFileName; - - return path; - } - - - @Override - public List listFiles(String parentPath) throws Exception { - try - { - parentPath = removeProtocol(parentPath); - com.dropbox.client2.DropboxAPI.Entry dirEntry = mApi.metadata(parentPath, 0, null, true, null); - - if (dirEntry.isDeleted) - throw new FileNotFoundException("Directory "+parentPath+" is deleted!"); - - List result = new ArrayList(); - - for (com.dropbox.client2.DropboxAPI.Entry e: dirEntry.contents) - { - if (e.isDeleted) - continue; - FileEntry fileEntry = convertToFileEntry(e); - result.add(fileEntry); - } - return result; - - - } catch (DropboxException e) { - - throw convertException(e); - } - - } - - private FileEntry convertToFileEntry(com.dropbox.client2.DropboxAPI.Entry e) { - //Log.d("JFS","e="+e); - FileEntry fileEntry = new FileEntry(); - fileEntry.canRead = true; - fileEntry.canWrite = true; - fileEntry.isDirectory = e.isDir; - fileEntry.sizeInBytes = e.bytes; - fileEntry.path = getProtocolId()+"://"+ e.path; - fileEntry.displayName = e.path.substring(e.path.lastIndexOf("/")+1); - //Log.d("JFS","fileEntry="+fileEntry); - Date lastModifiedDate = null; - if (e.modified != null) - lastModifiedDate = com.dropbox.client2.RESTUtility.parseDate(e.modified); - if (lastModifiedDate != null) - fileEntry.lastModifiedTime = lastModifiedDate.getTime(); - else - fileEntry.lastModifiedTime = -1; - //Log.d("JFS","Ok. Dir="+fileEntry.isDirectory); - return fileEntry; - } - - @Override - public void delete(String path) throws Exception { - try - { - path = removeProtocol(path); - mApi.delete(path); - } catch (DropboxException e) { - throw convertException(e); - } - - - } - - @Override - public FileEntry getFileEntry(String filename) throws Exception { - try - { - filename = removeProtocol(filename); - Log.d("KP2AJ", "getFileEntry(), mApi = "+mApi+" filename="+filename); - com.dropbox.client2.DropboxAPI.Entry dbEntry = mApi.metadata(filename, 0, null, false, null); - Log.d("JFS", "dbEntry = "+dbEntry); - - if (dbEntry.isDeleted) - throw new FileNotFoundException(filename+" is deleted!"); - - return convertToFileEntry(dbEntry); - - } catch (DropboxException e) { - - throw convertException(e); - } - } - - @Override - public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, - int requestCode) - { - - String path = getProtocolId()+":///"; - Log.d("KP2AJ", "startSelectFile "+path+", connected: "+path); - /*if (isConnected()) - { - Intent intent = new Intent(); - intent.putExtra(EXTRA_IS_FOR_SAVE, isForSave); - intent.putExtra(EXTRA_PATH, path); - activity.onImmediateResult(requestCode, RESULT_FILECHOOSER_PREPARED, intent); - } - else*/ - { - activity.startSelectFileProcess(path, isForSave, requestCode); - } - - - - } - - @Override - public String getProtocolId() { - return "dropbox"; - } - - public boolean requiresSetup(String path) - { - return !isConnected(); - } - - @Override - public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) { - if (isConnected()) - { - Intent intent = new Intent(); - intent.putExtra(EXTRA_PATH, path); - activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent); - } - else - { - activity.startFileUsageProcess(path, requestCode, alwaysReturnSuccess); - } - - } - - @Override - public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException { - if (!isConnected()) - { - throw new UserInteractionRequiredException(); - } - - } - - @Override - public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) { - - Log.d("KP2AJ", "OnCreate"); - - } - - @Override - public void onResume(FileStorageSetupActivity activity) { - - if (activity.getProcessName().equals(PROCESS_NAME_SELECTFILE)) - activity.getState().putString(EXTRA_PATH, activity.getPath()); - - Log.d("KP2AJ", "OnResume (3). LoggedIn="+mLoggedIn); - /*if (mLoggedIn) - { - finishActivityWithSuccess(activity); - return; - }*/ - - AndroidAuthSession session = mApi.getSession(); - - - JavaFileStorage.FileStorageSetupActivity storageSetupAct = (JavaFileStorage.FileStorageSetupActivity)activity; - - if (storageSetupAct.getState().containsKey("hasStartedAuth")) - { - Log.d("KP2AJ", "auth started"); - // The next part must be inserted in the onResume() method of the - // activity from which session.startAuthentication() was called, so - // that Dropbox authentication completes properly. - if (session.authenticationSuccessful()) { - Log.d("KP2AJ", "auth successful"); - try { - // Mandatory call to complete the auth - session.finishAuthentication(); - Log.d("KP2AJ", "finished auth "); - // Store it locally in our app for later use - TokenPair tokens = session.getAccessTokenPair(); - storeKeys(tokens.key, tokens.secret); - setLoggedIn(true); - Log.d("KP2AJ", "success"); - finishActivityWithSuccess(activity); - return; - - } catch (Exception e) { - Log.d("KP2AJ", "finish with error: " + e.toString()); - finishWithError(activity, e); - return; - } - } - - - Log.i(TAG, "authenticating not succesful"); - Intent data = new Intent(); - data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not succesful"); - ((Activity)activity).setResult(Activity.RESULT_CANCELED, data); - ((Activity)activity).finish(); - } - else - { - Log.d("KP2AJ", "Starting auth"); - mApi.getSession().startAuthentication(((Activity)activity)); - storageSetupAct.getState().putBoolean("hasStartedAuth", true); - - } - - - } - - @Override - public void onStart(FileStorageSetupActivity activity) { - - - } - - @Override - public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) { - //nothing to do here - - } - - String removeProtocol(String path) - { - if (path == null) - return null; - return path.substring(getProtocolId().length()+3); - } - - @Override - public String getDisplayName(String path) { - return path; - } - - @Override - public String getFilename(String path) throws Exception { - return path.substring(path.lastIndexOf("/")+1); - } - -} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/DropboxV2Storage.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/DropboxV2Storage.java new file mode 100644 index 00000000..3361c583 --- /dev/null +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/DropboxV2Storage.java @@ -0,0 +1,586 @@ +package keepass2android.javafilestorage; + +import com.dropbox.core.DbxAppInfo; +import com.dropbox.core.DbxException; +import com.dropbox.core.DbxOAuth1AccessToken; +import com.dropbox.core.DbxOAuth1Upgrader; +import com.dropbox.core.DbxRequestConfig; +import com.dropbox.core.InvalidAccessTokenException; +import com.dropbox.core.android.Auth; +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.http.OkHttp3Requestor; +import com.dropbox.core.v2.files.DeleteErrorException; +import com.dropbox.core.v2.files.DeletedMetadata; +import com.dropbox.core.v2.files.DownloadErrorException; +import com.dropbox.core.v2.files.FileMetadata; +import com.dropbox.core.v2.files.FolderMetadata; +import com.dropbox.core.v2.files.GetMetadataErrorException; +import com.dropbox.core.v2.files.ListFolderErrorException; +import com.dropbox.core.v2.files.ListFolderResult; +import com.dropbox.core.v2.files.Metadata; +import com.dropbox.core.v2.files.WriteMode; + + +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; + + +/** + * Created by Philipp on 18.11.2016. + */ +public class DropboxV2Storage extends JavaFileStorageBase +{ + private DbxAppInfo appInfo; + + public void bla() + { + + } + DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("kp2a") + .withHttpRequestor(OkHttp3Requestor.INSTANCE) + .build(); + + final static private String TAG = "KP2AJ"; + + final static private String ACCOUNT_PREFS_NAME = "prefs"; + final static private String ACCESS_KEY_V1_NAME = "ACCESS_KEY"; + final static private String ACCESS_SECRET_V1_NAME = "ACCESS_SECRET"; + final static private String ACCESS_TOKEN_NAME = "ACCESS_TOKEN_V2"; + + private boolean mLoggedIn = false; + private Context mContext; + + public FileEntry getRootFileEntry() { + + FileEntry rootEntry = new FileEntry(); + + rootEntry.displayName = ""; + rootEntry.isDirectory = true; + rootEntry.lastModifiedTime = -1; + rootEntry.canRead = rootEntry.canWrite = true; + rootEntry.path = getProtocolId()+":///"; + rootEntry.sizeInBytes = -1; + + return rootEntry; + } + + public enum AccessType { Full, AppFolder}; + + protected AccessType mAccessType = AccessType.Full; + + DbxClientV2 dbxClient; + + + public DropboxV2Storage(Context ctx, String _appKey, String _appSecret) + { + initialize(ctx, _appKey, _appSecret, false, mAccessType); + } + + public DropboxV2Storage(Context ctx, String _appKey, String _appSecret, boolean clearKeysOnStart) + { + initialize(ctx, _appKey, _appSecret, clearKeysOnStart, mAccessType); + } + + public DropboxV2Storage(Context ctx, String _appKey, String _appSecret, boolean clearKeysOnStart, AccessType accessType) + { + initialize(ctx, _appKey, _appSecret, clearKeysOnStart, accessType); + } + + private void initialize(Context ctx, String _appKey, String _appSecret, + boolean clearKeysOnStart, AccessType accessType) { + appInfo = new DbxAppInfo(_appKey,_appSecret); + mContext = ctx; + + if (clearKeysOnStart) + clearKeys(); + + this.mAccessType = accessType; + + buildSession(); + + } + + public boolean tryConnect(Activity activity) + { + if (!mLoggedIn) + Auth.startOAuth2Authentication(activity, appInfo.getKey()); + return mLoggedIn; + } + + private void setLoggedIn(boolean b) { + mLoggedIn = b; + + } + + + public boolean isConnected() + { + return mLoggedIn; + } + + + public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception + { + if ((previousFileVersion == null) || (previousFileVersion.equals(""))) + return false; + path = removeProtocol(path); + try { + Metadata entry = dbxClient.files().getMetadata(path); + return !String.valueOf(entry.hashCode()) .equals(previousFileVersion); + + } catch (DbxException e) { + throw convertException(e); + } + + } + + public String getCurrentFileVersionFast(String path) + { + try { + path = removeProtocol(path); + Metadata entry = dbxClient.files().getMetadata(path); + return String.valueOf(entry.hashCode()); + } catch (DbxException e) { + Log.d(TAG, e.toString()); + return ""; + } + } + + public InputStream openFileForRead(String path) throws Exception + { + try { + path = removeProtocol(path); + return dbxClient.files().download(path).getInputStream(); + } catch (DbxException e) { + //System.out.println("Something went wrong: " + e); + throw convertException(e); + } + } + + public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception + { + ByteArrayInputStream bis = new ByteArrayInputStream(data); + try { + path = removeProtocol(path); + + dbxClient.files().uploadBuilder(path).withMode(WriteMode.OVERWRITE).uploadAndFinish(bis); + + } catch (DbxException e) { + throw convertException(e); + } + } + + private Exception convertException(DbxException e) { + + Log.d(TAG, "Exception of type " +e.getClass().getName()+":" + e.getMessage()); + + if (InvalidAccessTokenException.class.isAssignableFrom(e.getClass()) ) { + Log.d(TAG, "LoggedIn=false (due to InvalidAccessTokenException)"); + setLoggedIn(false); + clearKeys(); + return new UserInteractionRequiredException("Unlinked from Dropbox! User must re-link.", e); + } + + if (ListFolderErrorException.class.isAssignableFrom(e.getClass()) ) { + ListFolderErrorException listFolderErrorException = (ListFolderErrorException)e; + if (listFolderErrorException.errorValue.getPathValue().isNotFound()) + return new FileNotFoundException(e.toString()); + } + if (DownloadErrorException.class.isAssignableFrom(e.getClass()) ) { + DownloadErrorException downloadErrorException = (DownloadErrorException)e; + if (downloadErrorException.errorValue.getPathValue().isNotFound()) + return new FileNotFoundException(e.toString()); + } + if (GetMetadataErrorException.class.isAssignableFrom(e.getClass()) ) { + GetMetadataErrorException getMetadataErrorException = (GetMetadataErrorException)e; + if (getMetadataErrorException.errorValue.getPathValue().isNotFound()) + return new FileNotFoundException(e.toString()); + } + if (DeleteErrorException.class.isAssignableFrom(e.getClass()) ) { + DeleteErrorException deleteErrorException = (DeleteErrorException)e; + if (deleteErrorException.errorValue.getPathLookupValue().isNotFound()) + return new FileNotFoundException(e.toString()); + } + + + return e; + } + + private void showToast(String msg) { + Toast error = Toast.makeText(mContext, msg, Toast.LENGTH_LONG); + error.show(); + } + + /** + * Keep the access keys returned from Trusted Authenticator in a local + * store, rather than storing user name & password, and re-authenticating each + * time (which is not to be done, ever). + * + * @return Array of [access_key, access_secret], or null if none stored + */ + private String[] getKeysV1() { + SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0); + String key = prefs.getString(ACCESS_KEY_V1_NAME, null); + String secret = prefs.getString(ACCESS_SECRET_V1_NAME, null); + if (key != null && secret != null) { + String[] ret = new String[2]; + ret[0] = key; + ret[1] = secret; + return ret; + } else { + return null; + } + } + + private String getKeyV2() { + SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0); + return prefs.getString(ACCESS_TOKEN_NAME, null); + } + + + private void storeKey(String v2token) { + Log.d(TAG, "Storing Dropbox accessToken"); + // Save the access key for later + SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0); + Editor edit = prefs.edit(); + edit.putString(ACCESS_TOKEN_NAME, v2token); + edit.commit(); + } + + private void clearKeys() { + SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0); + Editor edit = prefs.edit(); + edit.clear(); + edit.commit(); + } + + private void buildSession() { + + String v2Token = getKeyV2(); + + if (v2Token == null) { + String[] storedV1Keys = getKeysV1(); + if (storedV1Keys != null) { + DbxOAuth1AccessToken v1Token = new DbxOAuth1AccessToken(storedV1Keys[0], storedV1Keys[1]); + DbxOAuth1Upgrader upgrader = new DbxOAuth1Upgrader(requestConfig, appInfo); + try { + v2Token = upgrader.createOAuth2AccessToken(v1Token); + upgrader.disableOAuth1AccessToken(v1Token); + storeKey(v2Token); + } catch (DbxException e) { + + } + } + } + + if (v2Token != null) + { + dbxClient = new DbxClientV2(requestConfig, v2Token); + + setLoggedIn(true); + Log.d(TAG, "Creating Dropbox Session with accessToken"); + } else { + setLoggedIn(false); + Log.d(TAG, "Creating Dropbox Session without accessToken"); + } + + } + + @Override + public String createFolder(String parentPath, String newDirName) throws Exception { + try + { + String path = parentPath; + if (!path.endsWith("/")) + path = path + "/"; + path = path + newDirName; + + String pathWithoutProtocol = removeProtocol(path); + dbxClient.files().createFolder(pathWithoutProtocol); + + return path; + } + catch (DbxException e) { + throw convertException(e); + } + } + + @Override + public String createFilePath(String parentPath, String newFileName) throws Exception { + String path = parentPath; + if (!path.endsWith("/")) + path = path + "/"; + path = path + newFileName; + + return path; + } + + + @Override + public List listFiles(String parentPath) throws Exception { + try + { + parentPath = removeProtocol(parentPath); + if (parentPath.equals("/")) + parentPath = ""; //Dropbox is a bit picky here + ListFolderResult dirEntry = dbxClient.files().listFolder(parentPath); + + List result = new ArrayList(); + + while (true) + { + for (Metadata e: dirEntry.getEntries()) + { + FileEntry fileEntry = convertToFileEntry(e); + result.add(fileEntry); + } + + if (!dirEntry.getHasMore()) { + break; + } + + dirEntry = dbxClient.files().listFolderContinue(dirEntry.getCursor()); + } + + + return result; + + + } catch (DbxException e) { + + throw convertException(e); + } + + } + + private FileEntry convertToFileEntry(Metadata e) throws Exception { + //Log.d("JFS","e="+e); + + FileEntry fileEntry = new FileEntry(); + fileEntry.canRead = true; + fileEntry.canWrite = true; + if (e instanceof FolderMetadata) + { + FolderMetadata fm = (FolderMetadata)e; + fileEntry.isDirectory = true; + fileEntry.sizeInBytes = 0; + fileEntry.lastModifiedTime = 0; + } + else if (e instanceof FileMetadata) + { + FileMetadata fm = (FileMetadata)e; + fileEntry.sizeInBytes = fm.getSize(); + fileEntry.isDirectory = false; + fileEntry.lastModifiedTime = fm.getServerModified().getTime(); + } + else if (e instanceof DeletedMetadata) + { + throw new FileNotFoundException(); + } + else + { + throw new Exception("unexpected metadata " + e.getClass().getName() ); + } + + fileEntry.path = getProtocolId()+"://"+ e.getPathLower(); + fileEntry.displayName = e.getName(); + //Log.d("JFS","fileEntry="+fileEntry); + //Log.d("JFS","Ok. Dir="+fileEntry.isDirectory); + return fileEntry; + } + + @Override + public void delete(String path) throws Exception { + try + { + path = removeProtocol(path); + dbxClient.files().delete(path); + } catch (DbxException e) { + throw convertException(e); + } + + + } + + @Override + public FileEntry getFileEntry(String filename) throws Exception { + try + { + filename = removeProtocol(filename); + Log.d("KP2AJ", "getFileEntry(), " +filename); + + //querying root is not supported + if ((filename.equals("")) || (filename.equals("/"))) + return getRootFileEntry(); + + Metadata dbEntry = dbxClient.files().getMetadata(filename); + return convertToFileEntry(dbEntry); + + } catch (DbxException e) { + + throw convertException(e); + } + } + + @Override + public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, + int requestCode) + { + + String path = getProtocolId()+":///"; + Log.d("KP2AJ", "startSelectFile "+path+", connected: "+path); + /*if (isConnected()) + { + Intent intent = new Intent(); + intent.putExtra(EXTRA_IS_FOR_SAVE, isForSave); + intent.putExtra(EXTRA_PATH, path); + activity.onImmediateResult(requestCode, RESULT_FILECHOOSER_PREPARED, intent); + } + else*/ + { + activity.startSelectFileProcess(path, isForSave, requestCode); + } + + + + } + + @Override + public String getProtocolId() { + return "dropbox"; + } + + public boolean requiresSetup(String path) + { + return !isConnected(); + } + + @Override + public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) { + if (isConnected()) + { + Intent intent = new Intent(); + intent.putExtra(EXTRA_PATH, path); + activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent); + } + else + { + activity.startFileUsageProcess(path, requestCode, alwaysReturnSuccess); + } + + } + + @Override + public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException { + if (!isConnected()) + { + throw new UserInteractionRequiredException(); + } + + } + + @Override + public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) { + + Log.d("KP2AJ", "OnCreate"); + + } + + @Override + public void onResume(FileStorageSetupActivity activity) { + + if (activity.getProcessName().equals(PROCESS_NAME_SELECTFILE)) + activity.getState().putString(EXTRA_PATH, activity.getPath()); + + Log.d("KP2AJ", "OnResume (3). LoggedIn="+mLoggedIn); + /*if (mLoggedIn) + { + finishActivityWithSuccess(activity); + return; + }*/ + + JavaFileStorage.FileStorageSetupActivity storageSetupAct = (JavaFileStorage.FileStorageSetupActivity)activity; + + if (storageSetupAct.getState().containsKey("hasStartedAuth")) + { + Log.d("KP2AJ", "auth started"); + + String v2Token = Auth.getOAuth2Token(); + + + if (v2Token != null) { + Log.d("KP2AJ", "auth successful"); + try { + storeKey(v2Token); + buildSession(); + finishActivityWithSuccess(activity); + return; + + } catch (Exception e) { + Log.d("KP2AJ", "finish with error: " + e.toString()); + finishWithError(activity, e); + return; + } + } + + + Log.i(TAG, "authenticating not succesful"); + Intent data = new Intent(); + data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not succesful"); + ((Activity)activity).setResult(Activity.RESULT_CANCELED, data); + ((Activity)activity).finish(); + } + else + { + Log.d("KP2AJ", "Starting auth"); + Auth.startOAuth2Authentication((Activity)activity, appInfo.getKey()); + storageSetupAct.getState().putBoolean("hasStartedAuth", true); + + } + + + } + + @Override + public void onStart(FileStorageSetupActivity activity) { + + + } + + @Override + public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) { + //nothing to do here + + } + + String removeProtocol(String path) + { + if (path == null) + return null; + return path.substring(getProtocolId().length()+3); + } + + @Override + public String getDisplayName(String path) { + return path; + } + + @Override + public String getFilename(String path) throws Exception { + return path.substring(path.lastIndexOf("/")+1); + } + +} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/JavaFileStorageBase.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/JavaFileStorageBase.java index f598fd2b..5fc35823 100644 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/JavaFileStorageBase.java +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/JavaFileStorageBase.java @@ -9,7 +9,6 @@ import java.sql.Date; import java.text.SimpleDateFormat; import java.util.Calendar; -import org.apache.http.protocol.HTTP; import android.app.Activity; import android.content.Intent; @@ -18,6 +17,7 @@ import android.util.Log; public abstract class JavaFileStorageBase implements JavaFileStorage{ + protected static final String UTF_8 = "UTF-8"; private static final String ISO_8859_1 = "ISO-8859-1"; private static final String UTF8_PREFIX = ".U8-"; @@ -64,7 +64,7 @@ public abstract class JavaFileStorageBase implements JavaFileStorage{ protected String encode(final String unencoded) throws UnsupportedEncodingException { - return UTF8_PREFIX+java.net.URLEncoder.encode(unencoded, HTTP.UTF_8); + return UTF8_PREFIX+java.net.URLEncoder.encode(unencoded, UTF_8); } @@ -73,12 +73,14 @@ public abstract class JavaFileStorageBase implements JavaFileStorage{ //the first version of encode/decode used ISO 8859-1 which doesn't work with Cyrillic characters //this is why we need to check for the prefix, even though all new strings are UTF8 encoded. if (encodedString.startsWith(UTF8_PREFIX)) - return java.net.URLDecoder.decode(encodedString.substring(UTF8_PREFIX.length()), HTTP.UTF_8); + return java.net.URLDecoder.decode(encodedString.substring(UTF8_PREFIX.length()), UTF_8); else return java.net.URLDecoder.decode(encodedString, ISO_8859_1); } + + public class InvalidPathException extends Exception { /** diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/KitKatFileStorage.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/KitKatFileStorage.java deleted file mode 100644 index 1899daaf..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/KitKatFileStorage.java +++ /dev/null @@ -1,10 +0,0 @@ -package keepass2android.javafilestorage; - -import android.content.Context; - -public class KitKatFileStorage { - public KitKatFileStorage(Context ctx) - { -// ctx.getContentResolver(). - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/OneDriveStorage.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/OneDriveStorage.java new file mode 100644 index 00000000..045be4c5 --- /dev/null +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/OneDriveStorage.java @@ -0,0 +1,320 @@ +package keepass2android.javafilestorage; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.util.Log; +import android.widget.Toast; + +import com.onedrive.sdk.concurrency.ICallback; +import com.onedrive.sdk.core.ClientException; +import com.onedrive.sdk.core.DefaultClientConfig; +import com.onedrive.sdk.core.IClientConfig; +import com.onedrive.sdk.extensions.IItemCollectionPage; +import com.onedrive.sdk.extensions.IOneDriveClient; +import com.onedrive.sdk.extensions.Item; +import com.onedrive.sdk.extensions.OneDriveClient; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Philipp on 20.11.2016. + */ +public class OneDriveStorage extends JavaFileStorageBase +{ + final keepass2android.javafilestorage.onedrive.MyMSAAuthenticator msaAuthenticator = new keepass2android.javafilestorage.onedrive.MyMSAAuthenticator() { + @Override + public String getClientId() { + return "000000004010C234"; + } + + @Override + public String[] getScopes() { + return new String[] { "offline_access", "onedrive.readwrite" }; + } + }; + + IOneDriveClient oneDriveClient; + + public void bla(final Activity activity) { + android.util.Log.d("KP2A", "0"); + + android.util.Log.d("KP2A", "1"); + + android.util.Log.d("KP2A", "2"); + oneDriveClient + .getDrive() + .getRoot() + .buildRequest() + .get(new ICallback() { + @Override + public void success(final Item result) { + final String msg = "Found Root " + result.id; + Toast.makeText(activity, msg, Toast.LENGTH_SHORT) + .show(); + } + + @Override + public void failure(ClientException ex) { + Toast.makeText(activity, ex.toString(), Toast.LENGTH_SHORT) + .show(); + } + }); + android.util.Log.d("KP2A", "3"); + + } + + @Override + public boolean requiresSetup(String path) { + return !isConnected(); + } + + @Override + public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, int requestCode) { + String path = getProtocolId()+":///"; + Log.d("KP2AJ", "startSelectFile "+path+", connected: "+path); + if (isConnected()) + { + Intent intent = new Intent(); + intent.putExtra(EXTRA_IS_FOR_SAVE, isForSave); + intent.putExtra(EXTRA_PATH, path); + activity.onImmediateResult(requestCode, RESULT_FILECHOOSER_PREPARED, intent); + } + else + { + activity.startSelectFileProcess(path, isForSave, requestCode); + } + } + + private boolean isConnected() { + return msaAuthenticator.loginSilent() != null; + } + + + @Override + public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) { + if (isConnected()) + { + Intent intent = new Intent(); + intent.putExtra(EXTRA_PATH, path); + activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent); + } + else + { + activity.startFileUsageProcess(path, requestCode, alwaysReturnSuccess); + } + + } + + @Override + public String getProtocolId() { + return "onedrive"; + } + + @Override + public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException { + if (!isConnected()) + { + throw new UserInteractionRequiredException(); + } + + } + + @Override + public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) { + + Log.d("KP2AJ", "OnCreate"); + + } + + @Override + public void onResume(FileStorageSetupActivity activity) { + + if (activity.getProcessName().equals(PROCESS_NAME_SELECTFILE)) + activity.getState().putString(EXTRA_PATH, activity.getPath()); + + JavaFileStorage.FileStorageSetupActivity storageSetupAct = activity; + + if (storageSetupAct.getState().containsKey("hasStartedAuth")) + { + Log.d("KP2AJ", "auth started"); + + + if (oneDriveClient != null) { + Log.d("KP2AJ", "auth successful"); + try { + + finishActivityWithSuccess(activity); + return; + + } catch (Exception e) { + Log.d("KP2AJ", "finish with error: " + e.toString()); + finishWithError(activity, e); + return; + } + } + + + Log.i(TAG, "authenticating not succesful"); + Intent data = new Intent(); + data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not succesful"); + ((Activity)activity).setResult(Activity.RESULT_CANCELED, data); + ((Activity)activity).finish(); + } + else + { + Log.d("KP2AJ", "Starting auth"); + final IClientConfig oneDriveConfig = new DefaultClientConfig() { }; + + oneDriveClient = new OneDriveClient.Builder() + //.fromConfig(oneDriveConfig) + .authenticator(msaAuthenticator) + .executors(oneDriveConfig.getExecutors()) + .httpProvider(oneDriveConfig.getHttpProvider()) + .serializer(oneDriveConfig.getSerializer()) + .loginAndBuildClient((Activity)activity); + + storageSetupAct.getState().putBoolean("hasStartedAuth", true); + + } + + + } + + + String removeProtocol(String path) + { + if (path == null) + return null; + return path.substring(getProtocolId().length()+3); + } + + @Override + public String getDisplayName(String path) { + return path; + } + + @Override + public String getFilename(String path) throws Exception { + return path.substring(path.lastIndexOf("/")+1); + } + + @Override + public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception { + return false; + } + + @Override + public String getCurrentFileVersionFast(String path) { + return null; + } + + @Override + public InputStream openFileForRead(String path) throws Exception { + path = removeProtocol(path); + return oneDriveClient.getDrive() + .getRoot() + .getItemWithPath(path) + .getContent() + .buildRequest() + .get(); + } + + @Override + public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception { + path = removeProtocol(path); + oneDriveClient.getDrive() + .getRoot() + .getItemWithPath(path) + .getContent() + .buildRequest() + .put(data); + } + + @Override + public String createFolder(String parentPath, String newDirName) throws Exception { + throw new Exception("not implemented."); + } + + @Override + public String createFilePath(String parentPath, String newFileName) throws Exception { + String path = parentPath; + if (!path.endsWith("/")) + path = path + "/"; + path = path + newFileName; + + return path; + } + + @Override + public List listFiles(String parentPath) throws Exception { + ArrayList result = new ArrayList(); + parentPath = removeProtocol(parentPath); + IItemCollectionPage itemsPage = oneDriveClient.getDrive() + .getRoot() + .getItemWithPath(parentPath) + .getChildren() + .buildRequest() + .get(); + while (true) + { + List items = itemsPage.getCurrentPage(); + if (items.isEmpty()) + return result; + + itemsPage = itemsPage.getNextPage().buildRequest().get(); + + for (Item i: items) + { + FileEntry e = getFileEntry(getProtocolId() +"://"+ parentPath + "/" + i.name, i); + result.add(e); + } + } + } + + @NonNull + private FileEntry getFileEntry(String path, Item i) { + FileEntry e = new FileEntry(); + e.sizeInBytes = i.size; + e.displayName = i.name; + e.canRead = e.canWrite = true; + e.path = path; + e.isDirectory = i.folder != null; + return e; + } + + @Override + public FileEntry getFileEntry(String filename) throws Exception { + filename = removeProtocol(filename); + Item item = oneDriveClient.getDrive() + .getRoot() + .getItemWithPath(filename) + .buildRequest() + .get(); + return getFileEntry(filename, item); + } + + @Override + public void delete(String path) throws Exception { + path = removeProtocol(path); + oneDriveClient.getDrive() + .getRoot() + .getItemWithPath(path) + .buildRequest() + .delete(); + } + + @Override + public void onStart(FileStorageSetupActivity activity) { + + } + + @Override + public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) { + + } +} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/SftpStorage.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/SftpStorage.java index b568890c..142db95e 100644 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/SftpStorage.java +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/SftpStorage.java @@ -8,8 +8,6 @@ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; -import org.apache.http.protocol.HTTP; - import com.jcraft.jsch.Channel; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.ChannelSftp.LsEntry; @@ -307,13 +305,13 @@ public class SftpStorage extends JavaFileStorageBase { @Override protected String decode(String encodedString) throws UnsupportedEncodingException { - return java.net.URLDecoder.decode(encodedString, HTTP.UTF_8); + return java.net.URLDecoder.decode(encodedString, UTF_8); } @Override protected String encode(final String unencoded) throws UnsupportedEncodingException { - return java.net.URLEncoder.encode(unencoded, HTTP.UTF_8); + return java.net.URLEncoder.encode(unencoded, UTF_8); } ChannelSftp init(String filename) throws JSchException, UnsupportedEncodingException { @@ -321,7 +319,6 @@ public class SftpStorage extends JavaFileStorageBase { ConnectionInfo ci = splitStringToConnectionInfo(filename); Session session = jsch.getSession(ci.username, ci.host, ci.port); - UserInfo ui = new SftpUserInfo(ci.password); session.setUserInfo(ui); diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/SkyDriveFileStorage.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/SkyDriveFileStorage.java deleted file mode 100644 index 22f284d3..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/SkyDriveFileStorage.java +++ /dev/null @@ -1,844 +0,0 @@ -package keepass2android.javafilestorage; - -import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; - -import keepass2android.javafilestorage.skydrive.PrepareFileUsageListener; -import keepass2android.javafilestorage.skydrive.SkyDriveException; -import keepass2android.javafilestorage.skydrive.SkyDriveFile; -import keepass2android.javafilestorage.skydrive.SkyDriveFolder; -import keepass2android.javafilestorage.skydrive.SkyDriveObject; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import com.microsoft.live.LiveAuthClient; -import com.microsoft.live.LiveAuthException; -import com.microsoft.live.LiveAuthListener; -import com.microsoft.live.LiveConnectClient; -import com.microsoft.live.LiveConnectSession; -import com.microsoft.live.LiveDownloadOperation; -import com.microsoft.live.LiveOperation; -import com.microsoft.live.LiveOperationException; -import com.microsoft.live.LiveStatus; -import com.microsoft.live.OverwriteOption; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; -import android.os.Bundle; -import android.util.Log; - -public class SkyDriveFileStorage extends JavaFileStorageBase { - - private LiveAuthClient mAuthClient; - - private LiveConnectClient mConnectClient; - - private String mRootFolderId; - - private HashMap mFolderCache = new HashMap(); - - public static final String[] SCOPES = { "wl.signin", "wl.skydrive_update", "wl.offline_access"}; - - // see http://stackoverflow.com/questions/17997688/howto-to-parse-skydrive-api-date-in-java - SimpleDateFormat SKYDRIVE_DATEFORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ENGLISH); - - private Context mAppContext; - - private String mClientId; - - public final class JsonKeys { - public static final String CODE = "code"; - public static final String DATA = "data"; - public static final String DESCRIPTION = "description"; - public static final String ERROR = "error"; - public static final String EMAIL_HASHES = "email_hashes"; - public static final String FIRST_NAME = "first_name"; - public static final String GENDER = "gender"; - public static final String ID = "id"; - public static final String IS_FAVORITE = "is_favorite"; - public static final String IS_FRIEND = "is_friend"; - public static final String LAST_NAME = "last_name"; - public static final String LOCALE = "locale"; - public static final String LINK = "link"; - public static final String MESSAGE = "message"; - public static final String NAME = "name"; - public static final String UPDATED_TIME = "updated_time"; - public static final String USER_ID = "user_id"; - public static final String PERMISSIONS = "permissions"; - public static final String IS_DEFAULT = "is_default"; - public static final String FROM = "from"; - public static final String SUBSCRIPTION_LOCATION = "subscription_location"; - public static final String CREATED_TIME = "created_time"; - public static final String LOCATION = "location"; - public static final String TYPE = "type"; - public static final String PARENT_ID = "parent_id"; - public static final String SOURCE = "source"; - - private JsonKeys() { - throw new AssertionError(); - } - } - - class SkyDrivePath { - String mPath; - - public SkyDrivePath() { - } - - public SkyDrivePath(String path) throws UnsupportedEncodingException, - FileNotFoundException, InvalidPathException, - LiveOperationException, SkyDriveException { - setPath(path); - } - - public SkyDrivePath(String parentPath, JSONObject fileToAppend) - throws UnsupportedEncodingException, FileNotFoundException, - IOException, InvalidPathException, JSONException, - LiveOperationException, SkyDriveException { - setPath(parentPath); - - if ((!mPath.endsWith("/")) && (!mPath.equals(""))) - mPath = mPath + "/"; - mPath += encode(fileToAppend.getString("name")) + NAME_ID_SEP - + encode(fileToAppend.getString("id")); - } - - public void setPath(String path) throws UnsupportedEncodingException, - InvalidPathException, FileNotFoundException, - LiveOperationException, SkyDriveException { - setPathWithoutVerify(path); - verifyWithRetry(); - } - - private void verifyWithRetry() throws FileNotFoundException, - LiveOperationException, SkyDriveException, - UnsupportedEncodingException { - try { - verify(); - } catch (FileNotFoundException e) { - initializeFoldersCache(); - verify(); - } - } - - public void setPathWithoutVerify(String path) - throws UnsupportedEncodingException, InvalidPathException { - mPath = path.substring(getProtocolPrefix().length()); - logDebug( " mPath=" + mPath); - } - - // make sure the path exists - private void verify() throws FileNotFoundException, - UnsupportedEncodingException { - - if (mPath.equals("")) - return; - - String[] parts = mPath.split("/"); - - String parentId = mRootFolderId; - - for (int i = 0; i < parts.length; i++) { - String part = parts[i]; - logDebug( "parsing part " + part); - int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP); - if (indexOfSeparator < 0) - throw new FileNotFoundException("invalid path " + mPath); - String id = decode(part.substring(indexOfSeparator - + NAME_ID_SEP.length())); - String name = decode(part.substring(0, indexOfSeparator)); - logDebug( " name=" + name); - SkyDriveObject thisFolder = mFolderCache.get(id); - if (thisFolder == null) { - logDebug( "adding to cache"); - thisFolder = tryAddToCache(id); - - // check if it's still null - if (thisFolder == null) - throw new FileNotFoundException("couldn't find id " - + id + " being part of " + mPath - + " in SkyDrive "); - } - if (thisFolder.getParentId().equals(parentId) == false) - throw new FileNotFoundException("couldn't find parent id " - + parentId + " as parent of " - + thisFolder.getName() + " in " + mPath - + " in SkyDrive"); - if (thisFolder.getName().equals(name) == false) - throw new FileNotFoundException("Name of " + id - + " changed from " + name + " to " - + thisFolder.getName() + " in " + mPath - + " in SkyDrive "); - - parentId = id; - } - - } - - public String getDisplayName() { - // skydrive:// - String displayName = getProtocolPrefix(); - - if (mPath.equals("")) - return displayName; - - String[] parts = mPath.split("/"); - - for (int i = 0; i < parts.length; i++) { - String part = parts[i]; - logDebug("parsing part " + part); - int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP); - if (indexOfSeparator < 0) { - // seems invalid, but we're very generous here - displayName += "/" + part; - continue; - } - String name = part.substring(0, indexOfSeparator); - try { - name = decode(name); - } catch (UnsupportedEncodingException e) { - // ignore - } - displayName += "/" + name; - } - return displayName; - } - - public String getSkyDriveId() throws InvalidPathException, - UnsupportedEncodingException { - String pathWithoutTrailingSlash = mPath; - if (pathWithoutTrailingSlash.endsWith("/")) - pathWithoutTrailingSlash = pathWithoutTrailingSlash.substring( - 0, pathWithoutTrailingSlash.length() - 1); - if (pathWithoutTrailingSlash.equals("")) { - return mRootFolderId; - } - String lastPart = pathWithoutTrailingSlash - .substring(pathWithoutTrailingSlash - .lastIndexOf(NAME_ID_SEP) + NAME_ID_SEP.length()); - if (lastPart.contains("/")) - throw new InvalidPathException( - "error extracting SkyDriveId from " + mPath); - return decode(lastPart); - } - - public String getFullPath() throws UnsupportedEncodingException { - return getProtocolPrefix() + mPath; - } - - public SkyDrivePath getParentPath() throws UnsupportedEncodingException, FileNotFoundException, InvalidPathException, LiveOperationException, SkyDriveException { - String pathWithoutTrailingSlash = mPath; - if (pathWithoutTrailingSlash.endsWith("/")) - pathWithoutTrailingSlash = pathWithoutTrailingSlash.substring( - 0, pathWithoutTrailingSlash.length() - 1); - if (pathWithoutTrailingSlash.equals("")) - { - return null; - } - int indexOfLastSlash = pathWithoutTrailingSlash.lastIndexOf("/"); - if (indexOfLastSlash == -1) - { - return new SkyDrivePath(getProtocolPrefix()); - } - String parentPath = pathWithoutTrailingSlash.substring(0, indexOfLastSlash); - return new SkyDrivePath(getProtocolPrefix()+parentPath); - } - - public String getFilename() throws InvalidPathException { - String pathWithoutTrailingSlash = mPath; - if (pathWithoutTrailingSlash.endsWith("/")) - pathWithoutTrailingSlash = pathWithoutTrailingSlash.substring( - 0, pathWithoutTrailingSlash.length() - 1); - - String[] parts = mPath.split("/"); - - String lastPart = parts[parts.length-1]; - int indexOfSeparator = lastPart.lastIndexOf(NAME_ID_SEP); - if (indexOfSeparator < 0) { - throw new InvalidPathException("cannot extract filename from " + mPath); - } - String name = lastPart.substring(0, indexOfSeparator); - try { - name = decode(name); - } catch (UnsupportedEncodingException e) { - // ignore - } - return name; - - - } - - }; - - public SkyDriveFileStorage(String clientId, Context appContext) { - logDebug("Constructing SkyDriveFileStorage"); - mAuthClient = new LiveAuthClient(appContext, clientId); - mAppContext = appContext; - mClientId = clientId; - - } - - void login(final FileStorageSetupActivity activity) { - logDebug("skydrive login"); - mAuthClient.login((Activity) activity, Arrays.asList(SCOPES), - new LiveAuthListener() { - @Override - public void onAuthComplete(LiveStatus status, - LiveConnectSession session, Object userState) { - if (status == LiveStatus.CONNECTED) { - initialize(activity, session); - } else { - finishWithError(activity, new Exception( - "Error connecting to SkdDrive. Status is " - + status)); - } - } - - @Override - public void onAuthError(LiveAuthException exception, - Object userState) { - finishWithError(activity, exception); - } - }); - } - - private void initialize(final FileStorageSetupActivity setupAct, - LiveConnectSession session) { - logDebug("skydrive initialize"); - mConnectClient = new LiveConnectClient(session); - - final Activity activity = (Activity)setupAct; - - AsyncTask > task = new AsyncTask>() - { - - @Override - protected AsyncTaskResult doInBackground(Object... arg0) { - try { - initializeFoldersCache(); - if (setupAct.getProcessName().equals(PROCESS_NAME_SELECTFILE)) - setupAct.getState().putString(EXTRA_PATH, getProtocolPrefix()); - return new AsyncTaskResult("ok"); - } catch ( Exception anyError) { - return new AsyncTaskResult(anyError); - } - } - - @Override - protected void onPostExecute(AsyncTaskResult result) { - Exception error = result.getError(); - if (error != null ) { - finishWithError(setupAct, error); - } else if ( isCancelled()) { - activity.setResult(Activity.RESULT_CANCELED); - activity.finish(); - } else { - //all right! - finishActivityWithSuccess(setupAct); - } - } - }; - - task.execute(new Object[]{}); - - } - - - - private void initializeFoldersCache() throws LiveOperationException, - SkyDriveException, FileNotFoundException { - - logDebug("skydrive initializeFoldersCache"); - //use alias for now (overwritten later): - mRootFolderId = "me/skydrive"; - - LiveOperation operation = mConnectClient.get(mRootFolderId + "/files"); - - JSONObject result = operation.getResult(); - checkResult(result); - - mFolderCache.clear(); - - JSONArray data = result.optJSONArray(JsonKeys.DATA); - for (int i = 0; i < data.length(); i++) { - SkyDriveObject skyDriveObj = SkyDriveObject.create(data - .optJSONObject(i)); - if (skyDriveObj == null) - continue; // ignored type - logDebug( "adding "+skyDriveObj.getName()+" to cache with id " + skyDriveObj.getId()+" in "+skyDriveObj.getParentId()); - mFolderCache.put(skyDriveObj.getId(), skyDriveObj); - - mRootFolderId = skyDriveObj.getParentId(); - } - - //check if we received anything. If not: query the root folder directly - if (data.length() == 0) - { - operation = mConnectClient.get(mRootFolderId); - result = operation.getResult(); - checkResult(result); - mRootFolderId = SkyDriveObject.create(result).getId(); - - } - } - - private void checkResult(JSONObject result) throws SkyDriveException, FileNotFoundException { - if (result.has(JsonKeys.ERROR)) { - JSONObject error = result.optJSONObject(JsonKeys.ERROR); - String message = error.optString(JsonKeys.MESSAGE); - String code = error.optString(JsonKeys.CODE); - logDebug( "Code: "+code); - if ("resource_not_found".equals(code)) - throw new FileNotFoundException(message); - else - throw new SkyDriveException(message, code); - } - } - - private SkyDriveObject tryAddToCache(String skyDriveId) { - try { - SkyDriveObject obj = getSkyDriveObject(skyDriveId); - if (obj != null) { - mFolderCache.put(obj.getId(), obj); - } - return obj; - } catch (Exception e) { - return null; - } - - } - - private SkyDriveObject getSkyDriveObject(SkyDrivePath skyDrivePath) - throws LiveOperationException, InvalidPathException, - UnsupportedEncodingException, SkyDriveException, FileNotFoundException { - String skyDriveID = skyDrivePath.getSkyDriveId(); - return getSkyDriveObject(skyDriveID); - } - - private SkyDriveObject getSkyDriveObject(String skyDriveID) - throws LiveOperationException, SkyDriveException, - FileNotFoundException { - LiveOperation operation = mConnectClient.get(skyDriveID); - JSONObject result = operation.getResult(); - checkResult(result); - SkyDriveObject obj = SkyDriveObject.create(result); - return obj; - } - - @Override - public boolean requiresSetup(String path) { - // always go through the setup process: - return true; - } - - @Override - public void startSelectFile(FileStorageSetupInitiatorActivity activity, - boolean isForSave, int requestCode) { - - ((JavaFileStorage.FileStorageSetupInitiatorActivity) (activity)) - .startSelectFileProcess(getProtocolId() + "://", isForSave, - requestCode); - - } - - @Override - public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, - String path, int requestCode, boolean alwaysReturnSuccess) { - - //tell the activity which requests the file usage that it must launch the FileStorageSetupActivity - // which will then go through the onCreate/onStart/onResume process which is used by our FileStorage - ((JavaFileStorage.FileStorageSetupInitiatorActivity) (activity)) - .startFileUsageProcess(path, requestCode, alwaysReturnSuccess); - - } - @Override - public void prepareFileUsage(Context appContext, String path) throws Exception - { - - PrepareFileUsageListener listener = new PrepareFileUsageListener(); - - mAuthClient.initializeSynchronous(Arrays.asList(SCOPES), listener, null); - - - if (listener.exception != null) - throw listener.exception; - - if (listener.status == LiveStatus.CONNECTED) { - - mConnectClient = new LiveConnectClient(listener.session); - if (mFolderCache.isEmpty()) - { - initializeFoldersCache(); - } - - } else { - if (listener.status == LiveStatus.NOT_CONNECTED) - logDebug( "not connected"); - else if (listener.status == LiveStatus.UNKNOWN) - logDebug( "unknown"); - else - logDebug( "unexpected status " + listener.status); - - throw new UserInteractionRequiredException(); - - } - - - } - - @Override - public String getProtocolId() { - return "skydrive"; - } - - @Override - public String getDisplayName(String path) { - - SkyDrivePath skydrivePath = new SkyDrivePath(); - try { - skydrivePath.setPathWithoutVerify(path); - } - catch (Exception e) - { - e.printStackTrace(); - return path; - } - - return skydrivePath.getDisplayName(); - - } - - @Override - public boolean checkForFileChangeFast(String path, - String previousFileVersion) throws Exception { - - String currentVersion = getCurrentFileVersionFast(path); - if (currentVersion == null) - return false; - return currentVersion.equals(previousFileVersion) == false; - } - - @Override - public String getCurrentFileVersionFast(String path) { - try - { - SkyDrivePath drivePath = new SkyDrivePath(path); - SkyDriveObject obj = getSkyDriveObject(drivePath); - if (obj == null) - return null; - return obj.getUpdatedTime(); - } - catch (Exception e) - { - logDebug("Error getting file version."); - Log.w(TAG,"Error getting file version:"); - e.printStackTrace(); - return null; - } - - } - - @Override - public InputStream openFileForRead(String path) throws Exception { - try - { - logDebug("openFileForRead: " + path); - LiveDownloadOperation op = mConnectClient.download(new SkyDrivePath(path).getSkyDriveId()+"/content"); - logDebug("openFileForRead ok" + path); - return op.getStream(); - } - catch (Exception e) - { - throw convertException(e); - } - } - - @Override - public void uploadFile(String path, byte[] data, boolean writeTransactional) - throws Exception { - - try - { - logDebug("uploadFile to "+path); - SkyDrivePath driveTargetPath = new SkyDrivePath(path); - SkyDrivePath driveUploadPath = driveTargetPath; - SkyDrivePath driveTempPath = null; - ByteArrayInputStream bis = new ByteArrayInputStream(data); - - //if writeTransactional, upload the file to a temp destination. - //this is a somewhat ugly way because it requires two uploads, but renaming/copying doesn't work - //nicely in SkyDrive, and SkyDrive doesn't provide file histories by itself, so we need to make sure - //no file gets corrupt if upload is canceled. - if (writeTransactional) - { - LiveOperation uploadOp = uploadFile(driveUploadPath.getParentPath(), driveUploadPath.getFilename()+".tmp", bis); - driveTempPath = new SkyDrivePath(driveUploadPath.getParentPath().getFullPath(), uploadOp.getResult()); - //recreate ByteArrayInputStream for use in uploadFile below - bis = new ByteArrayInputStream(data); - } - - //upload the file - uploadFile(driveUploadPath.getParentPath(), driveUploadPath.getFilename(), bis); - - if (writeTransactional) - { - //delete old file - mConnectClient.delete(driveTempPath.getSkyDriveId()); - // don't check result. If delete fails -> not a big deal - } - - } - catch (Exception e) - { - throw convertException(e); - } - - } - - private LiveOperation uploadFile(SkyDrivePath parentPath, String filename, ByteArrayInputStream bis) - throws LiveOperationException, InvalidPathException, - UnsupportedEncodingException, FileNotFoundException, - SkyDriveException { - LiveOperation op = mConnectClient.upload(parentPath.getSkyDriveId(), filename, bis, OverwriteOption.Overwrite); - checkResult(op.getResult()); - return op; - } - - @Override - public String createFolder(String parentPath, String newDirName) - throws Exception { - - try { - SkyDrivePath skyDriveParentPath = new SkyDrivePath(parentPath); - - String parentId = skyDriveParentPath.getSkyDriveId(); - - JSONObject newFolder = new JSONObject(); - newFolder.put("name", newDirName); - newFolder.put("description", "folder"); - - LiveOperation operation = mConnectClient.post( - parentId, newFolder); - JSONObject result = operation.getResult(); - checkResult(result); - return new SkyDrivePath(parentPath, result).getFullPath(); - } catch (Exception e) { - throw convertException(e); - } - - } - - private Exception convertException(Exception e) throws Exception { - - e.printStackTrace(); - logDebug(e.toString()); - Log.w(TAG, e); - - throw e; - } - - @Override - public String createFilePath(String parentPath, String newFileName) - throws Exception { - try { - SkyDrivePath skyDriveParentPath = new SkyDrivePath(parentPath); - - LiveOperation op = uploadFile(skyDriveParentPath, newFileName, new ByteArrayInputStream(new byte[0])); - checkResult(op.getResult()); - - return new SkyDrivePath(parentPath, op.getResult()).getFullPath(); - } catch (Exception e) { - throw convertException(e); - } - } - - @Override - public List listFiles(String parentPath) throws Exception { - - try - { - SkyDrivePath parentDrivePath = new SkyDrivePath(parentPath); - LiveOperation operation = mConnectClient.get(parentDrivePath.getSkyDriveId() + "/files"); - - JSONObject result = operation.getResult(); - checkResult(result); - - JSONArray data = result.optJSONArray(JsonKeys.DATA); - List resultList = new ArrayList(data.length()); - - for (int i = 0; i < data.length(); i++) { - SkyDriveObject skyDriveObj = SkyDriveObject.create(data - .optJSONObject(i)); - if (skyDriveObj == null) - continue; // ignored type - logDebug( "listing "+skyDriveObj.getName()+" with id " + skyDriveObj.getId()+" in "+skyDriveObj.getParentId()); - - resultList.add(convertToFileEntry(parentDrivePath, skyDriveObj)); - } - return resultList; - } - catch (Exception e) - { - throw convertException(e); - } - } - - private FileEntry convertToFileEntry(SkyDrivePath parentPath, SkyDriveObject skyDriveObj) throws UnsupportedEncodingException, FileNotFoundException, IOException, InvalidPathException, JSONException, LiveOperationException, SkyDriveException { - - FileEntry res = new FileEntry(); - res.canRead = true; - res.canWrite = true; - res.displayName = skyDriveObj.getName(); - res.isDirectory = SkyDriveFolder.class.isAssignableFrom(skyDriveObj.getClass()); - - try - { - res.lastModifiedTime = SKYDRIVE_DATEFORMATTER.parse(skyDriveObj.getUpdatedTime()).getTime(); - } - catch (Exception e) - { - Log.w(TAG, "Cannot parse time " + skyDriveObj.getUpdatedTime()); - logDebug("Cannot parse time! " + e.toString()); - res.lastModifiedTime = -1; - } - if (parentPath == null) //this is the case if we're listing the parent path itself - res.path = getProtocolPrefix(); - else - res.path = new SkyDrivePath(parentPath.getFullPath(), skyDriveObj.toJson()).getFullPath(); - logDebug( "path: "+res.path); - if (SkyDriveFile.class.isAssignableFrom(skyDriveObj.getClass())) - { - res.sizeInBytes = ((SkyDriveFile)skyDriveObj).getSize(); - } - - return res; - } - - @Override - public FileEntry getFileEntry(String filename) throws Exception { - try - { - SkyDrivePath drivePath = new SkyDrivePath(filename); - logDebug( "getFileEntry for "+ filename +" = "+drivePath.getFullPath()); - logDebug( " parent is "+drivePath.getParentPath()); - return convertToFileEntry(drivePath.getParentPath(),getSkyDriveObject(drivePath)); - } - catch (Exception e) - { - throw convertException(e); - } - - } - - @Override - public void delete(String path) throws Exception { - try - { - SkyDrivePath drivePath = new SkyDrivePath(path); - LiveOperation op = mConnectClient.delete(drivePath.getSkyDriveId()); - checkResult(op.getResult()); - } - catch (Exception e) - { - throw convertException(e); - } - - - } - - @Override - public void onCreate(FileStorageSetupActivity activity, - Bundle savedInstanceState) { - - } - - @Override - public void onResume(FileStorageSetupActivity activity) { - - } - - - @Override - public void onStart(final FileStorageSetupActivity activity) { - try - { - logDebug("skydrive onStart"); - initialize(activity); - } - catch (Exception e) - { - finishWithError(activity, e); - } - } - - private void initialize(final FileStorageSetupActivity activity) { - mAuthClient.initialize(Arrays.asList(SCOPES), new LiveAuthListener() { - @Override - public void onAuthError(LiveAuthException exception, - Object userState) { - finishWithError(( activity), exception); - } - - @Override - public void onAuthComplete(LiveStatus status, - LiveConnectSession session, Object userState) { - - if (status == LiveStatus.CONNECTED) { - logDebug("connected!"); - initialize(activity, session); - - } else { - if (status == LiveStatus.NOT_CONNECTED) - logDebug( "not connected"); - else if (status == LiveStatus.UNKNOWN) - logDebug( "unknown"); - else - logDebug( "unexpected status " + status); - try - { - login(activity); - } - catch (IllegalStateException e) - { - //this may happen if an un-cancelled login progress is already in progress. - //however, the activity might have been destroyed, so try again with another auth client next time - logDebug("IllegalStateException: Recreating AuthClient"); - mAuthClient = new LiveAuthClient(mAppContext, mClientId); - finishWithError(activity, e); - } - catch (Exception e) - { - finishWithError(activity, e); - } - } - } - }); - } - - @Override - public void onActivityResult(FileStorageSetupActivity activity, - int requestCode, int resultCode, Intent data) { - - } - - @Override - public String getFilename(String path) throws Exception { - SkyDrivePath p = new SkyDrivePath(); - p.setPathWithoutVerify(path); - return p.getFilename(); - } - -} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/WebDavStorage.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/WebDavStorage.java new file mode 100644 index 00000000..7183aa47 --- /dev/null +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/WebDavStorage.java @@ -0,0 +1,435 @@ +package keepass2android.javafilestorage; + +import android.content.Context; +import android.content.Intent; + +import android.os.Bundle; +import android.util.Log; + +import com.burgstaller.okhttp.AuthenticationCacheInterceptor; +import com.burgstaller.okhttp.CachingAuthenticatorDecorator; +import com.burgstaller.okhttp.DispatchingAuthenticator; +import com.burgstaller.okhttp.basic.BasicAuthenticator; +import com.burgstaller.okhttp.digest.CachingAuthenticator; +import com.burgstaller.okhttp.digest.DigestAuthenticator; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; +import java.util.concurrent.ConcurrentHashMap; + +import keepass2android.javafilestorage.webdav.PropfindXmlParser; +import keepass2android.javafilestorage.webdav.WebDavUtil; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +public class WebDavStorage extends JavaFileStorageBase { + + public String buildFullPath(String url, String username, String password) throws UnsupportedEncodingException { + String scheme = url.substring(0, url.indexOf("://")); + url = url.substring(scheme.length() + 3); + return scheme + "://" + encode(username)+":"+encode(password)+"@"+url; + } + + static class ConnectionInfo { + String URL; + String username; + String password; + } + + private ConnectionInfo splitStringToConnectionInfo(String filename) + throws UnsupportedEncodingException { + ConnectionInfo ci = new ConnectionInfo(); + + String scheme = filename.substring(0, filename.indexOf("://")); + filename = filename.substring(scheme.length() + 3); + String userPwd = filename.substring(0, filename.indexOf('@')); + ci.username = decode(userPwd.substring(0, userPwd.indexOf(":"))); + ci.password = decode(userPwd.substring(userPwd.indexOf(":") + 1)); + ci.URL = scheme + "://" +filename.substring(filename.indexOf('@') + 1); + return ci; + } + + + private static final String HTTP_PROTOCOL_ID = "http"; + private static final String HTTPS_PROTOCOL_ID = "https"; + + @Override + public boolean checkForFileChangeFast(String path, + String previousFileVersion) throws Exception { + String currentVersion = getCurrentFileVersionFast(path); + if (currentVersion == null) + return false; + return currentVersion.equals(previousFileVersion) == false; + } + + @Override + public String getCurrentFileVersionFast(String path) { + + return null; // no simple way to get the version "fast" + } + + + @Override + public InputStream openFileForRead(String path) throws Exception { + try { + ConnectionInfo ci = splitStringToConnectionInfo(path); + + Request request = new Request.Builder() + .url(new URL(ci.URL)) + .method("GET", null) + .build(); + + Response response = getClient(ci).newCall(request).execute(); + checkStatus(response); + return response.body().byteStream(); + } catch (Exception e) { + throw convertException(e); + } + } + + private OkHttpClient getClient(ConnectionInfo ci) { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + final Map authCache = new ConcurrentHashMap<>(); + + com.burgstaller.okhttp.digest.Credentials credentials = new com.burgstaller.okhttp.digest.Credentials(ci.username, ci.password); + final BasicAuthenticator basicAuthenticator = new BasicAuthenticator(credentials); + final DigestAuthenticator digestAuthenticator = new DigestAuthenticator(credentials); + + // note that all auth schemes should be registered as lowercase! + DispatchingAuthenticator authenticator = new DispatchingAuthenticator.Builder() + .with("digest", digestAuthenticator) + .with("basic", basicAuthenticator) + .build(); + OkHttpClient client = builder + .authenticator(new CachingAuthenticatorDecorator(authenticator, authCache)) + .addInterceptor(new AuthenticationCacheInterceptor(authCache)) + .build(); + return client; + } + + + @Override + public void uploadFile(String path, byte[] data, boolean writeTransactional) + throws Exception { + + try { + ConnectionInfo ci = splitStringToConnectionInfo(path); + + Request request = new Request.Builder() + .url(new URL(ci.URL)) + .put(RequestBody.create(MediaType.parse("application/binary"), data)) + .build(); + + //TODO consider writeTransactional + //TODO check for error + + Response response = getClient(ci).newCall(request).execute(); + checkStatus(response); + } catch (Exception e) { + throw convertException(e); + } + + } + + @Override + public String createFolder(String parentPath, String newDirName) + throws Exception { + + try { + String newFolder = createFilePath(parentPath, newDirName); + ConnectionInfo ci = splitStringToConnectionInfo(newFolder); + + Request request = new Request.Builder() + .url(new URL(ci.URL)) + .method("MKCOL", null) + .build(); + + Response response = getClient(ci).newCall(request).execute(); + checkStatus(response); + return newFolder; + } catch (Exception e) { + throw convertException(e); + } + + + } + + private String concatPaths(String parentPath, String newDirName) { + String res = parentPath; + if (!res.endsWith("/")) + res += "/"; + res += newDirName; + return res; + } + + @Override + public String createFilePath(String parentPath, String newFileName) + throws Exception { + if (parentPath.endsWith("/") == false) + parentPath += "/"; + return parentPath + newFileName; + } + + public List listFiles(String parentPath, int depth) throws Exception { + ArrayList result = new ArrayList<>(); + try { + if (parentPath.endsWith("/")) + parentPath = parentPath.substring(0,parentPath.length()-1); + + ConnectionInfo ci = splitStringToConnectionInfo(parentPath); + String requestBody = "\n" + + "\n" + + " \n" + + "\n"; + Log.d("WEBDAV", "starting query for " + ci.URL); + Request request = new Request.Builder() + .url(new URL(ci.URL)) + .method("PROPFIND", RequestBody.create(MediaType.parse("application/xml"),requestBody)) + .addHeader("Depth",String.valueOf(depth)) + + .build(); + + Response response = getClient(ci).newCall(request).execute(); + + checkStatus(response); + + String xml = response.body().string(); + + PropfindXmlParser parser = new PropfindXmlParser(); + List responses = parser.parse(new StringReader(xml)); + + for (PropfindXmlParser.Response r: responses) + { + PropfindXmlParser.Response.PropStat.Prop okprop =r.getOkProp(); + if (okprop != null) + { + FileEntry e = new FileEntry(); + e.canRead = e.canWrite = true; + Date lastMod = WebDavUtil.parseDate(okprop.LastModified); + if (lastMod != null) + e.lastModifiedTime = lastMod.getTime(); + if (okprop.ContentLength != null) + { + try { + e.sizeInBytes = Integer.parseInt(okprop.ContentLength); + } catch (NumberFormatException exc) { + e.sizeInBytes = -1; + } + } + e.isDirectory = r.href.endsWith("/"); + + e.displayName = okprop.DisplayName; + if (e.displayName == null) + { + e.displayName = getDisplayName(r.href); + } + e.path = r.href; + + if (e.path.indexOf("://") == -1) + { + //relative path: + e.path = buildPathFromHref(parentPath, r.href); + } + + if ((depth == 1) && e.isDirectory) + { + String path = e.path; + if (!path.endsWith("/")) + path += "/"; + + String parentPathWithTrailingSlash = parentPath + "/"; + + //for depth==1 only list children, not directory itself + if (path.equals(parentPathWithTrailingSlash)) + continue; + } + + result.add(e); + } + } + return result; + + + } catch (Exception e) { + throw convertException(e); + } + + } + + private String buildPathFromHref(String parentPath, String href) throws UnsupportedEncodingException { + String scheme = parentPath.substring(0, parentPath.indexOf("://")); + String filename = parentPath.substring(scheme.length() + 3); + String userPwd = filename.substring(0, filename.indexOf('@')); + String username_enc = (userPwd.substring(0, userPwd.indexOf(":"))); + String password_enc = (userPwd.substring(userPwd.indexOf(":") + 1)); + + String host = filename.substring(filename.indexOf('@')+1); + int firstSlashPos = host.indexOf("/"); + if (firstSlashPos >= 0) + { + host = host.substring(0,firstSlashPos); + } + if (!href.startsWith("/")) + href = "/" + href; + + return scheme + "://" + username_enc + ":" + password_enc + "@" + host + href; + } + + @Override + public List listFiles(String parentPath) throws Exception { + return listFiles(parentPath, 1); + } + + private void checkStatus(Response response) throws Exception { + if((response.code() < 200) + || (response.code() >= 300)) + { + if (response.code() == 404) + throw new FileNotFoundException(); + throw new Exception("Received unexpected response: " + response.toString()); + } + } + + + private Exception convertException(Exception e) { + + return e; + + } + + @Override + public FileEntry getFileEntry(String filename) throws Exception { + List list = listFiles(filename,0); + if (list.size() != 1) + throw new FileNotFoundException(); + return list.get(0); + } + + @Override + public void delete(String path) throws Exception { + + try { + ConnectionInfo ci = splitStringToConnectionInfo(path); + + Request request = new Request.Builder() + .url(new URL(ci.URL)) + .delete() + .build(); + + Response response = getClient(ci).newCall(request).execute(); + + checkStatus(response); + } catch (Exception e) { + throw convertException(e); + } + + } + + @Override + public void startSelectFile( + JavaFileStorage.FileStorageSetupInitiatorActivity activity, + boolean isForSave, int requestCode) { + activity.performManualFileSelect(isForSave, requestCode, getProtocolId()); + } + + @Override + protected String decode(String encodedString) + throws UnsupportedEncodingException { + return java.net.URLDecoder.decode(encodedString, UTF_8); + } + + @Override + protected String encode(final String unencoded) + throws UnsupportedEncodingException { + return java.net.URLEncoder.encode(unencoded, UTF_8); + } + + + @Override + public void prepareFileUsage(JavaFileStorage.FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) { + Intent intent = new Intent(); + intent.putExtra(EXTRA_PATH, path); + activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent); + } + + @Override + public String getProtocolId() { + return HTTPS_PROTOCOL_ID; + } + + @Override + public void onResume(JavaFileStorage.FileStorageSetupActivity setupAct) { + + } + + @Override + public boolean requiresSetup(String path) { + return false; + } + + @Override + public void onCreate(FileStorageSetupActivity activity, + Bundle savedInstanceState) { + + } + + @Override + public String getDisplayName(String path) { + if (path.endsWith("/")) + path = path.substring(0, path.length()-1); + int lastIndex = path.lastIndexOf("/"); + if (lastIndex >= 0) + return path.substring(lastIndex + 1); + else + return path; + + } + + @Override + public String getFilename(String path) throws Exception { + if (path.endsWith("/")) + path = path.substring(0, path.length() - 1); + int lastIndex = path.lastIndexOf("/"); + if (lastIndex >= 0) + return path.substring(lastIndex + 1); + else + return path; + } + + @Override + public void onStart(FileStorageSetupActivity activity) { + + } + + @Override + public void onActivityResult(FileStorageSetupActivity activity, + int requestCode, int resultCode, Intent data) { + + + } + + + @Override + public void prepareFileUsage(Context appContext, String path) { + //nothing to do + + } + +} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/onedrive/MyMSAAccountInfo.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/onedrive/MyMSAAccountInfo.java new file mode 100644 index 00000000..42f4ac77 --- /dev/null +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/onedrive/MyMSAAccountInfo.java @@ -0,0 +1,102 @@ +package keepass2android.javafilestorage.onedrive; + +/** + * Created by Philipp on 22.11.2016. + */ + +import com.microsoft.services.msa.LiveConnectSession; +import com.onedrive.sdk.authentication.AccountType; +import com.onedrive.sdk.authentication.IAccountInfo; +import com.onedrive.sdk.authentication.MSAAccountInfo; +import com.onedrive.sdk.authentication.MSAAuthenticator; +import com.onedrive.sdk.logger.ILogger; + +import com.microsoft.services.msa.LiveConnectSession; +import com.onedrive.sdk.logger.ILogger; + +/** + * Account information for a MSA based account. + */ +public class MyMSAAccountInfo implements IAccountInfo { + + /** + * The service root for the OneDrive personal API. + */ + public static final String ONE_DRIVE_PERSONAL_SERVICE_ROOT = "https://api.onedrive.com/v1.0"; + + /** + * The authenticator that can refresh this account. + */ + private final MyMSAAuthenticator mAuthenticator; + + /** + * The session this account is based off of. + */ + private LiveConnectSession mSession; + + /** + * The logger. + */ + private final ILogger mLogger; + + /** + * Creates an MSAAccountInfo object. + * @param authenticator The authenticator that this account info was created from. + * @param liveConnectSession The session this account is based off of. + * @param logger The logger. + */ + public MyMSAAccountInfo(final MyMSAAuthenticator authenticator, + final LiveConnectSession liveConnectSession, + final ILogger logger) { + mAuthenticator = authenticator; + mSession = liveConnectSession; + mLogger = logger; + } + + /** + * Get the type of the account. + * @return The MicrosoftAccount account type. + */ + @Override + public AccountType getAccountType() { + return AccountType.MicrosoftAccount; + } + + /** + * Get the access token for requests against the service root. + * @return The access token for requests against the service root. + */ + @Override + public String getAccessToken() { + return mSession.getAccessToken(); + } + + /** + * Get the OneDrive service root for this account. + * @return the OneDrive service root for this account. + */ + @Override + public String getServiceRoot() { + return ONE_DRIVE_PERSONAL_SERVICE_ROOT; + } + + /** + * Indicates if the account access token is expired and needs to be refreshed. + * @return true if refresh() needs to be called and + * false if the account is still valid. + */ + @Override + public boolean isExpired() { + return mSession.isExpired(); + } + + /** + * Refreshes the authentication token for this account info. + */ + @Override + public void refresh() { + mLogger.logDebug("Refreshing access token..."); + final MyMSAAccountInfo newInfo = (MyMSAAccountInfo)mAuthenticator.loginSilent(); + mSession = newInfo.mSession; + } +} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/onedrive/MyMSAAuthenticator.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/onedrive/MyMSAAuthenticator.java new file mode 100644 index 00000000..e29d1652 --- /dev/null +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/onedrive/MyMSAAuthenticator.java @@ -0,0 +1,437 @@ +package keepass2android.javafilestorage.onedrive; + +/** + * Created by Philipp on 22.11.2016. + */ +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; + +import com.microsoft.onedrivesdk.BuildConfig; +import com.microsoft.services.msa.LiveAuthClient; +import com.microsoft.services.msa.LiveAuthException; +import com.microsoft.services.msa.LiveAuthListener; +import com.microsoft.services.msa.LiveConnectSession; +import com.microsoft.services.msa.LiveStatus; +import com.onedrive.sdk.authentication.ClientAuthenticatorException; +import com.onedrive.sdk.authentication.IAccountInfo; +import com.onedrive.sdk.authentication.IAuthenticator; +import com.onedrive.sdk.authentication.MSAAccountInfo; +import com.onedrive.sdk.concurrency.ICallback; +import com.onedrive.sdk.core.ClientException; +import com.onedrive.sdk.concurrency.SimpleWaiter; +import com.onedrive.sdk.concurrency.IExecutors; +import com.onedrive.sdk.core.OneDriveErrorCodes; +import com.onedrive.sdk.http.IHttpProvider; +import com.onedrive.sdk.logger.ILogger; + +import java.security.InvalidParameterException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Wrapper around the MSA authentication library. + * https://github.com/MSOpenTech/msa-auth-for-android + */ +@SuppressWarnings("ThrowableResultOfMethodCallIgnored") +public abstract class MyMSAAuthenticator implements IAuthenticator { + + + /** + * The sign in cancellation message. + */ + private static final String SIGN_IN_CANCELLED_MESSAGE = "The user cancelled the login operation."; + + /** + * The preferences for this authenticator. + */ + private static final String MSA_AUTHENTICATOR_PREFS = "MSAAuthenticatorPrefs"; + + /** + * The key for the user id. + */ + private static final String USER_ID_KEY = "userId"; + + /** + * The key for the version code + */ + public static final String VERSION_CODE_KEY = "versionCode"; + + /** + * The default user id + */ + private static final String DEFAULT_USER_ID = "@@defaultUser"; + + /** + * The active user id. + */ + private final AtomicReference mUserId = new AtomicReference<>(); + + /** + * The executors. + */ + private IExecutors mExecutors; + + /** + * Indicates whether this authenticator has been initialized. + */ + private boolean mInitialized; + + /** + * The context UI interactions should happen with. + */ + private Activity mActivity; + + /** + * The logger. + */ + private ILogger mLogger; + + /** + * The client id for this authenticator. + * https://dev.onedrive.com/auth/msa_oauth.htm#to-register-your-app + * @return The client id. + */ + public abstract String getClientId(); + + /** + * The scopes for this application. + * https://dev.onedrive.com/auth/msa_oauth.htm#authentication-scopes + * @return The scopes for this application. + */ + public abstract String[] getScopes(); + + /** + * The live authentication client. + */ + private LiveAuthClient mAuthClient; + + /** + * Initializes the authenticator. + * @param executors The executors to schedule foreground and background tasks. + * @param httpProvider The http provider for sending requests. + * @param activity The activity to create interactive UI on. + * @param logger The logger for diagnostic information. + */ + @Override + public synchronized void init(final IExecutors executors, + final IHttpProvider httpProvider, + final Activity activity, + final ILogger logger) { + if (mInitialized) { + return; + } + + mExecutors = executors; + mActivity = activity; + mLogger = logger; + mInitialized = true; + mAuthClient = new LiveAuthClient(activity, getClientId(), Arrays.asList(getScopes())); + + final SharedPreferences prefs = getSharedPreferences(); + mUserId.set(prefs.getString(USER_ID_KEY, null)); + } + + /** + * Starts an interactive login asynchronously. + * @param emailAddressHint The hint for the email address during the interactive login. + * @param loginCallback The callback to be called when the login is complete. + */ + @Override + public void login(final String emailAddressHint, final ICallback loginCallback) { + if (!mInitialized) { + throw new IllegalStateException("init must be called"); + } + + if (loginCallback == null) { + throw new InvalidParameterException("loginCallback"); + } + + mLogger.logDebug("Starting login async"); + + mExecutors.performOnBackground(new Runnable() { + @Override + public void run() { + try { + mExecutors.performOnForeground(login(emailAddressHint), loginCallback); + } catch (final ClientException e) { + mExecutors.performOnForeground(e, loginCallback); + } + } + }); + } + + /** + * Starts an interactive login. + * @param emailAddressHint The hint for the email address during the interactive login. + * @return The account info. + * @throws ClientException An exception occurs if the login was unable to complete for any reason. + */ + @Override + public synchronized IAccountInfo login(final String emailAddressHint) throws ClientException { + if (!mInitialized) { + throw new IllegalStateException("init must be called"); + } + + mLogger.logDebug("Starting login"); + + final AtomicReference error = new AtomicReference<>(); + final SimpleWaiter waiter = new SimpleWaiter(); + + final LiveAuthListener listener = new LiveAuthListener() { + @Override + public void onAuthComplete(final LiveStatus liveStatus, + final LiveConnectSession liveConnectSession, + final Object o) { + if (liveStatus == LiveStatus.NOT_CONNECTED) { + mLogger.logDebug("Received invalid login failure from silent authentication with MSA, ignoring."); + } else { + mLogger.logDebug("Successful interactive login"); + waiter.signal(); + } + } + + @Override + public void onAuthError(final LiveAuthException e, + final Object o) { + OneDriveErrorCodes code = OneDriveErrorCodes.AuthenticationFailure; + if (e.getError().equals(SIGN_IN_CANCELLED_MESSAGE)) { + code = OneDriveErrorCodes.AuthenticationCancelled; + } + + error.set(new ClientAuthenticatorException("Unable to login with MSA", e, code)); + mLogger.logError(error.get().getMessage(), error.get()); + waiter.signal(); + } + }; + + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mAuthClient.login(mActivity, /* scopes */null, /* user object */ null, emailAddressHint, listener); + } + }); + + mLogger.logDebug("Waiting for MSA callback"); + waiter.waitForSignal(); + + final ClientException exception = error.get(); + if (exception != null) { + throw exception; + } + + final String userId; + if (emailAddressHint != null) { + userId = emailAddressHint; + } else { + userId = DEFAULT_USER_ID; + } + + mUserId.set(userId); + + final SharedPreferences prefs = getSharedPreferences(); + prefs.edit() + .putString(USER_ID_KEY, mUserId.get()) + .putInt(VERSION_CODE_KEY, BuildConfig.VERSION_CODE) + .apply(); + + return getAccountInfo(); + } + + /** + * Starts a silent login asynchronously. + * @param loginCallback The callback to be called when the login is complete. + */ + @Override + public void loginSilent(final ICallback loginCallback) { + if (!mInitialized) { + throw new IllegalStateException("init must be called"); + } + + if (loginCallback == null) { + throw new InvalidParameterException("loginCallback"); + } + + mLogger.logDebug("Starting login silent async"); + + mExecutors.performOnBackground(new Runnable() { + @Override + public void run() { + try { + mExecutors.performOnForeground(loginSilent(), loginCallback); + } catch (final ClientException e) { + mExecutors.performOnForeground(e, loginCallback); + } + } + }); + } + + /** + * Starts a silent login. + * @return The account info. + * @throws ClientException An exception occurs if the login was unable to complete for any reason. + */ + @Override + public synchronized IAccountInfo loginSilent() throws ClientException { + if (!mInitialized) { + throw new IllegalStateException("init must be called"); + } + + mLogger.logDebug("Starting login silent"); + + final int userIdStoredMinVersion = 10112; + if (getSharedPreferences().getInt(VERSION_CODE_KEY, 0) >= userIdStoredMinVersion + && mUserId.get() == null) { + mLogger.logDebug("No login information found for silent authentication"); + return null; + } + + final SimpleWaiter loginSilentWaiter = new SimpleWaiter(); + final AtomicReference error = new AtomicReference<>(); + + final boolean waitForCallback = mAuthClient.loginSilent(new LiveAuthListener() { + @Override + public void onAuthComplete(final LiveStatus liveStatus, + final LiveConnectSession liveConnectSession, + final Object o) { + if (liveStatus == LiveStatus.NOT_CONNECTED) { + error.set(new ClientAuthenticatorException("Failed silent login, interactive login required", + OneDriveErrorCodes.AuthenticationFailure)); + mLogger.logError(error.get().getMessage(), error.get()); + } else { + mLogger.logDebug("Successful silent login"); + } + loginSilentWaiter.signal(); + } + + @Override + public void onAuthError(final LiveAuthException e, + final Object o) { + OneDriveErrorCodes code = OneDriveErrorCodes.AuthenticationFailure; + if (e.getError().equals(SIGN_IN_CANCELLED_MESSAGE)) { + code = OneDriveErrorCodes.AuthenticationCancelled; + } + + error.set(new ClientAuthenticatorException("Login silent authentication error", e, code)); + mLogger.logError(error.get().getMessage(), error.get()); + loginSilentWaiter.signal(); + } + }); + + if (!waitForCallback) { + mLogger.logDebug("MSA silent auth fast-failed"); + return null; + } + + mLogger.logDebug("Waiting for MSA callback"); + loginSilentWaiter.waitForSignal(); + final ClientException exception = error.get(); + if (exception != null) { + throw exception; + } + + return getAccountInfo(); + } + + /** + * Log the current user out. + * @param logoutCallback The callback to be called when the logout is complete. + */ + @Override + public void logout(final ICallback logoutCallback) { + if (!mInitialized) { + throw new IllegalStateException("init must be called"); + } + + if (logoutCallback == null) { + throw new InvalidParameterException("logoutCallback"); + } + + mLogger.logDebug("Starting logout async"); + + mExecutors.performOnBackground(new Runnable() { + @Override + public void run() { + try { + logout(); + mExecutors.performOnForeground((Void) null, logoutCallback); + } catch (final ClientException e) { + mExecutors.performOnForeground(e, logoutCallback); + } + } + }); + } + + /** + * Log the current user out. + * @throws ClientException An exception occurs if the logout was unable to complete for any reason. + */ + @Override + public synchronized void logout() throws ClientException { + if (!mInitialized) { + throw new IllegalStateException("init must be called"); + } + + mLogger.logDebug("Starting logout"); + + final SimpleWaiter logoutWaiter = new SimpleWaiter(); + final AtomicReference error = new AtomicReference<>(); + mAuthClient.logout(new LiveAuthListener() { + @Override + public void onAuthComplete(final LiveStatus liveStatus, + final LiveConnectSession liveConnectSession, + final Object o) { + mLogger.logDebug("Logout completed"); + logoutWaiter.signal(); + } + + @Override + public void onAuthError(final LiveAuthException e, final Object o) { + error.set(new ClientAuthenticatorException("MSA Logout failed", + e, + OneDriveErrorCodes.AuthenticationFailure)); + mLogger.logError(error.get().getMessage(), error.get()); + logoutWaiter.signal(); + } + }); + + mLogger.logDebug("Waiting for logout to complete"); + logoutWaiter.waitForSignal(); + + mLogger.logDebug("Clearing all MSA Authenticator shared preferences"); + final SharedPreferences prefs = getSharedPreferences(); + prefs.edit() + .clear() + .putInt(VERSION_CODE_KEY, BuildConfig.VERSION_CODE) + .apply(); + mUserId.set(null); + + final ClientException exception = error.get(); + if (exception != null) { + throw exception; + } + } + + /** + * Gets the current account info for this authenticator. + * @return NULL if no account is available. + */ + @Override + public IAccountInfo getAccountInfo() { + final LiveConnectSession session = mAuthClient.getSession(); + if (session == null) { + return null; + } + + return new MyMSAAccountInfo(this, session, mLogger); + } + + /** + * Gets the shared preferences for this authenticator. + * @return The shared preferences. + */ + private SharedPreferences getSharedPreferences() { + return mActivity.getSharedPreferences(MSA_AUTHENTICATOR_PREFS, Context.MODE_PRIVATE); + } + +} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/PrepareFileUsageListener.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/PrepareFileUsageListener.java deleted file mode 100644 index baf7442f..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/PrepareFileUsageListener.java +++ /dev/null @@ -1,37 +0,0 @@ -package keepass2android.javafilestorage.skydrive; - -import com.microsoft.live.LiveAuthException; -import com.microsoft.live.LiveAuthListener; -import com.microsoft.live.LiveConnectSession; -import com.microsoft.live.LiveStatus; - -public class PrepareFileUsageListener implements LiveAuthListener { - - public Exception exception; - public LiveStatus status; - - volatile boolean done; - public LiveConnectSession session; - - public boolean isDone() - { - return done; - } - - @Override - public void onAuthError(LiveAuthException _exception, - Object userState) { - exception = _exception; - done = true; - } - - @Override - public void onAuthComplete(LiveStatus _status, - LiveConnectSession _session, Object userState) - { - status = _status; - session = _session; - done = true; - } - -} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveException.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveException.java deleted file mode 100644 index adfe464f..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveException.java +++ /dev/null @@ -1,22 +0,0 @@ -package keepass2android.javafilestorage.skydrive; - -public class SkyDriveException extends Exception { - - /** - * - */ - private static final long serialVersionUID = 4594684204315150764L; - private String mCode; - - public SkyDriveException(String message, String code) - { - super(message); - mCode = code; - } - - public String getCode() { - return mCode; - } - - -} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveFile.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveFile.java deleted file mode 100644 index 96028fcc..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveFile.java +++ /dev/null @@ -1,33 +0,0 @@ -package keepass2android.javafilestorage.skydrive; - - -import org.json.JSONObject; - -public class SkyDriveFile extends SkyDriveObject { - - public static final String TYPENAME = "file"; - - public SkyDriveFile(JSONObject file) { - super(file); - } - - public long getSize() { - return mObject.optLong("size"); - } - - public int getCommentsCount() { - return mObject.optInt("comments_count"); - } - - public boolean getCommentsEnabled() { - return mObject.optBoolean("comments_enabled"); - } - - public String getSource() { - return mObject.optString("source"); - } - - public boolean getIsEmbeddable() { - return mObject.optBoolean("is_embeddable"); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveFolder.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveFolder.java deleted file mode 100644 index 48641717..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveFolder.java +++ /dev/null @@ -1,15 +0,0 @@ -package keepass2android.javafilestorage.skydrive; - -import org.json.JSONObject; - -public class SkyDriveFolder extends SkyDriveObject { - public static final String TYPENAME = "folder"; - - public SkyDriveFolder(JSONObject object) { - super(object); - } - - public int getCount() { - return mObject.optInt("count"); - } -} diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveObject.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveObject.java deleted file mode 100644 index cb5e2bb0..00000000 --- a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/skydrive/SkyDriveObject.java +++ /dev/null @@ -1,114 +0,0 @@ -package keepass2android.javafilestorage.skydrive; - - - -import org.json.JSONObject; - -public abstract class SkyDriveObject { - - public static class From { - private final JSONObject mFrom; - - public From(JSONObject from) { - assert from != null; - mFrom = from; - } - - public String getName() { - return mFrom.optString("name"); - } - - public String getId() { - return mFrom.optString("id"); - } - - public JSONObject toJson() { - return mFrom; - } - } - - public static class SharedWith { - private final JSONObject mSharedWidth; - - public SharedWith(JSONObject sharedWith) { - assert sharedWith != null; - mSharedWidth = sharedWith; - } - - public String getAccess() { - return mSharedWidth.optString("access"); - } - - public JSONObject toJson() { - return mSharedWidth; - } - } - - - public static SkyDriveObject create(JSONObject skyDriveObject) { - String type = skyDriveObject.optString("type"); - - if (type.equals(SkyDriveFolder.TYPENAME)) { - return new SkyDriveFolder(skyDriveObject); - } else if (type.equals(SkyDriveFile.TYPENAME)) { - return new SkyDriveFile(skyDriveObject); - } else return null; - } - - protected final JSONObject mObject; - - public SkyDriveObject(JSONObject object) { - assert object != null; - mObject = object; - } - - public String getId() { - return mObject.optString("id"); - } - - public From getFrom() { - return new From(mObject.optJSONObject("from")); - } - - public String getName() { - return mObject.optString("name"); - } - - public String getParentId() { - return mObject.optString("parent_id"); - } - - public String getDescription() { - return mObject.isNull("description") ? null : mObject.optString("description"); - } - - public String getType() { - return mObject.optString("type"); - } - - public String getLink() { - return mObject.optString("link"); - } - - public String getCreatedTime() { - return mObject.optString("created_time"); - } - - public String getUpdatedTime() { - return mObject.optString("updated_time"); - } - - public String getUploadLocation() { - return mObject.optString("upload_location"); - } - - public SharedWith getSharedWith() { - return new SharedWith(mObject.optJSONObject("shared_with")); - } - - public JSONObject toJson() { - return mObject; - } - - -} \ No newline at end of file diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/webdav/PropfindXmlParser.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/webdav/PropfindXmlParser.java new file mode 100644 index 00000000..59ed2034 --- /dev/null +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/webdav/PropfindXmlParser.java @@ -0,0 +1,230 @@ +package keepass2android.javafilestorage.webdav; + +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Philipp on 21.09.2016. + */ +public class PropfindXmlParser +{ + private final String ns = "DAV:"; + + public static class Response + { + public Response() + { + propstat = new ArrayList(); + } + + public String href; + + public URL getAbsoluteUri(URL requestUrl) throws MalformedURLException { + String serverUrl = requestUrl.getProtocol() + "://" + requestUrl.getHost(); + if (requestUrl.getPort() > 0) + serverUrl += ":" + requestUrl.getPort(); + return new URL(new URL(serverUrl),href); + } + + public static class PropStat { + + public PropStat() + { + prop = new Prop(); + } + + + public boolean isOk() + { + if (status == null) + return false; + String[] parts = status.split(" "); + if (parts.length < 2) + return false; + return parts[1].equals("200"); + } + + public static class Prop { + public String DisplayName; + public String LastModified; + public String ContentLength; + } + public String status; + public Prop prop; + } + + ArrayList propstat; + + public PropStat.Prop getOkProp() + { + for (PropStat p: propstat) + { + if (p.isOk()) + return p.prop; + } + return null; + } + + } + + public List parse(Reader in) throws XmlPullParserException, IOException { + List responses = new ArrayList(); + + XmlPullParser parser = Xml.newPullParser(); + + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,true); + parser.setInput(in); + parser.nextTag(); + + parser.require(XmlPullParser.START_TAG, ns, "multistatus"); + + + while (parser.next() != XmlPullParser.END_TAG) { + android.util.Log.d("PARSE", "1eventtype=" + parser.getEventType()); + + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + + android.util.Log.d("PARSE", "1name = " + name); + + // Starts by looking for the entry tag + if (name.equals("response")) { + responses.add(readResponse(parser)); + } else { + skip(parser); + } + } + + + return responses; + } + + private Response readResponse(XmlPullParser parser) throws IOException, XmlPullParserException { + Response response = new Response(); + + parser.require(XmlPullParser.START_TAG, ns, "response"); + + android.util.Log.d("PARSE", "readResponse"); + + while (parser.next() != XmlPullParser.END_TAG) { + android.util.Log.d("PARSE", "2eventtype=" + parser.getEventType()); + + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + + + android.util.Log.d("PARSE", "2name=" + name); + + + if (name.equals("href")) { + response.href = readText(parser); + } else if (name.equals("propstat")) { + response.propstat.add(readPropStat(parser)); + } else { + skip(parser); + } + } + return response; + + } + + + // For the tags title and summary, extracts their text values. + private String readText(XmlPullParser parser) throws IOException, XmlPullParserException { + String result = ""; + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + android.util.Log.d("PARSE", "read text " + result); + return result; + } + + + private Response.PropStat readPropStat(XmlPullParser parser) throws IOException, XmlPullParserException { + Response.PropStat propstat = new Response.PropStat(); + parser.require(XmlPullParser.START_TAG, ns, "propstat"); + android.util.Log.d("PARSE", "readPropStat"); + while (parser.next() != XmlPullParser.END_TAG) { + + android.util.Log.d("PARSE", "3eventtype=" + parser.getEventType()); + + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + android.util.Log.d("PARSE", "3name=" + name); + if (name.equals("prop")) + { + propstat.prop = readProp(parser); + } else if (name.equals("status")) + { + propstat.status = readText(parser); + } else { + skip(parser); + } + } + return propstat; + } + + private Response.PropStat.Prop readProp(XmlPullParser parser) throws IOException, XmlPullParserException { + Response.PropStat.Prop prop = new Response.PropStat.Prop(); + parser.require(XmlPullParser.START_TAG, ns, "prop"); + android.util.Log.d("PARSE", "readProp"); + while (parser.next() != XmlPullParser.END_TAG) { + android.util.Log.d("PARSE", "eventtype=" + parser.getEventType()); + + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + + android.util.Log.d("PARSE", "4name = " + name); + if (name.equals("getcontentlength")) + { + prop.ContentLength = readText(parser); + } else if (name.equals("getlastmodified")) { + prop.LastModified = readText(parser); + } else if (name.equals("displayname")) { + prop.DisplayName = readText(parser); + } else { + skip(parser); + } + } + + return prop; + } + + private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + android.util.Log.d("PARSE", "skipping " + parser.getName()); + + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } +} + diff --git a/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/webdav/WebDavUtil.java b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/webdav/WebDavUtil.java new file mode 100644 index 00000000..4abd42d7 --- /dev/null +++ b/src/java/JavaFileStorage/app/src/main/java/keepass2android/javafilestorage/webdav/WebDavUtil.java @@ -0,0 +1,115 @@ +package keepass2android.javafilestorage.webdav; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +public class WebDavUtil { + /** + * Date formats using for Date parsing. + */ + private static final List> DATETIME_FORMATS = Arrays.asList( + new ThreadLocal() + { + @Override + protected SimpleDateFormat initialValue() + { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + }, + new ThreadLocal() + { + @Override + protected SimpleDateFormat initialValue() + { + SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + }, + new ThreadLocal() + { + @Override + protected SimpleDateFormat initialValue() + { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + }, + new ThreadLocal() + { + @Override + protected SimpleDateFormat initialValue() + { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + }, + new ThreadLocal() + { + @Override + protected SimpleDateFormat initialValue() + { + SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + }, + new ThreadLocal() + { + @Override + protected SimpleDateFormat initialValue() + { + SimpleDateFormat format = new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + }, + new ThreadLocal() + { + @Override + protected SimpleDateFormat initialValue() + { + SimpleDateFormat format = new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + } + ); + + /** + * Loops over all the possible date formats and tries to find the right one. + * + * @param value ISO date string + * @return Null if there is a parsing failure + */ + public static Date parseDate(String value) + { + if (value == null) + { + return null; + } + Date date = null; + for (ThreadLocal format : DATETIME_FORMATS) + { + try + { + date = format.get().parse(value); + break; + } + catch (ParseException e) + { + // We loop through this until we found a valid one. + } + } + return date; + } +} diff --git a/src/java/JavaFileStorage/app/src/main/res/values/strings.xml b/src/java/JavaFileStorage/app/src/main/res/values/strings.xml index 51a5f8cc..f11f7450 100644 --- a/src/java/JavaFileStorage/app/src/main/res/values/strings.xml +++ b/src/java/JavaFileStorage/app/src/main/res/values/strings.xml @@ -1,5 +1,3 @@ - JavaFileStorage - diff --git a/src/java/android-filechooser-AS/.idea/.name b/src/java/android-filechooser-AS/.idea/.name index 04698307..4626d284 100644 --- a/src/java/android-filechooser-AS/.idea/.name +++ b/src/java/android-filechooser-AS/.idea/.name @@ -1 +1 @@ -code \ No newline at end of file +android-filechooser-AS \ No newline at end of file diff --git a/src/java/android-filechooser-AS/.idea/gradle.xml b/src/java/android-filechooser-AS/.idea/gradle.xml index 3ed2e6cb..0f858804 100644 --- a/src/java/android-filechooser-AS/.idea/gradle.xml +++ b/src/java/android-filechooser-AS/.idea/gradle.xml @@ -5,14 +5,7 @@ + diff --git a/src/java/android-filechooser-AS/.idea/libraries/support_v4_18_0_0.xml b/src/java/android-filechooser-AS/.idea/libraries/support_v4_18_0_0.xml deleted file mode 100644 index f8f83da7..00000000 --- a/src/java/android-filechooser-AS/.idea/libraries/support_v4_18_0_0.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/java/android-filechooser-AS/.idea/misc.xml b/src/java/android-filechooser-AS/.idea/misc.xml index 8f712fe7..d5124e1e 100644 --- a/src/java/android-filechooser-AS/.idea/misc.xml +++ b/src/java/android-filechooser-AS/.idea/misc.xml @@ -1,7 +1,28 @@ - - + + + diff --git a/src/java/android-filechooser-AS/.idea/modules.xml b/src/java/android-filechooser-AS/.idea/modules.xml index 8528435a..f33e7d23 100644 --- a/src/java/android-filechooser-AS/.idea/modules.xml +++ b/src/java/android-filechooser-AS/.idea/modules.xml @@ -3,7 +3,6 @@ - \ No newline at end of file diff --git a/src/java/android-filechooser-AS/.idea/vcs.xml b/src/java/android-filechooser-AS/.idea/vcs.xml deleted file mode 100644 index 6564d52d..00000000 --- a/src/java/android-filechooser-AS/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/java/android-filechooser-AS/.idea/workspace.xml b/src/java/android-filechooser-AS/.idea/workspace.xml index 93bbbdf7..4058d83b 100644 --- a/src/java/android-filechooser-AS/.idea/workspace.xml +++ b/src/java/android-filechooser-AS/.idea/workspace.xml @@ -1,9 +1,10 @@ - - + + + - + - - - - - - - - - - - + - + - + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - + @@ -92,1270 +63,6 @@ - - - - @@ -1363,22 +70,10 @@ - - - - - - - + - - @@ -1543,22 +167,30 @@ @@ -1576,22 +208,6 @@ - - - - - - - @@ -1613,11 +229,48 @@ + + + + + + + + + + + + + + + + - - 1458677251506 + + 1474396196987 - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1728,18 +410,10 @@ - - - - - - - - - + @@ -1752,10 +426,18 @@ - + - + + + + + + + + + @@ -1763,39 +445,15 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/src/java/android-filechooser-AS/android-filechooser-AS.iml b/src/java/android-filechooser-AS/android-filechooser-AS.iml index 5ca97873..a41844bf 100644 --- a/src/java/android-filechooser-AS/android-filechooser-AS.iml +++ b/src/java/android-filechooser-AS/android-filechooser-AS.iml @@ -7,6 +7,16 @@ diff --git a/src/java/android-filechooser-AS/app/build.gradle b/src/java/android-filechooser-AS/app/build.gradle index bf256a58..2dad25fc 100644 --- a/src/java/android-filechooser-AS/app/build.gradle +++ b/src/java/android-filechooser-AS/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion 23 - buildToolsVersion "23.0.0" + buildToolsVersion "23.0.2" defaultConfig { minSdkVersion 15 @@ -11,12 +11,12 @@ android { buildTypes { release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' } } } dependencies { - compile 'com.android.support:support-v4:18.0.0' + compile 'com.android.support:support-v4:23.0.0' } diff --git a/src/java/android-filechooser-AS/build.gradle b/src/java/android-filechooser-AS/build.gradle index 16f0b42e..8b36472d 100644 --- a/src/java/android-filechooser-AS/build.gradle +++ b/src/java/android-filechooser-AS/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.2.3' + classpath 'com.android.tools.build:gradle:2.1.3' } } allprojects { @@ -14,4 +14,7 @@ allprojects { } dependencies { +} +android { + buildToolsVersion '23.0.2' } \ No newline at end of file diff --git a/src/java/android-filechooser-AS/gradle/wrapper/gradle-wrapper.properties b/src/java/android-filechooser-AS/gradle/wrapper/gradle-wrapper.properties index 0c71e760..1acea939 100644 --- a/src/java/android-filechooser-AS/gradle/wrapper/gradle-wrapper.properties +++ b/src/java/android-filechooser-AS/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 10 15:27:10 PDT 2013 +#Tue Sep 20 20:32:06 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/src/java/android-filechooser-AS/local.properties b/src/java/android-filechooser-AS/local.properties index ce2e28e1..dc7c5d44 100644 --- a/src/java/android-filechooser-AS/local.properties +++ b/src/java/android-filechooser-AS/local.properties @@ -1,7 +1,11 @@ -## This file must *NOT* be checked into Version Control Systems, +## This file is automatically generated by Android Studio. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must *NOT* be checked into Version Control Systems, # as it contains information specific to your local configuration. # # Location of the SDK. This is only used by Gradle. -# -#Tue Mar 22 21:07:31 CET 2016 -sdk.dir=C\:\\Users\\Philipp\\AppData\\Local\\Android\\android-sdk +# For customization when using a Version Control System, please read the +# header note. +#Tue Sep 20 20:29:56 CEST 2016 +sdk.dir=C\:\\Users\\Philipp\\AppData\\Local\\Xamarin\\Universal\\AndroidSDK