Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:vimeo/vimeo-networking-java into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
kvenn committed Mar 16, 2016
2 parents d71130f + 496b360 commit 9161b7d
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 33 deletions.
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,16 @@ compile project(':vimeo-networking-java:vimeo-networking')
```

### Initialization

The `VimeoClient` instance is highly customizable through the use of the `Configuration.Builder` class. There are a few different default `Builder` constructors that cover core functionality.
In the below sections, we cover examples of ways to customize your `VimeoClient` instance. Full implementations are in the example app.

#### Configuration Builder for Apps with Account Management
On app launch, configure `VimeoClient` with your client key, secret, and scope strings. And once initialization is complete, authenticate if necessary. This authentication can include [client credentials](#client-credentials-grant) or authentication via [code grant](#oauth-authorization-code-grant).

If you choose to pass in an `AccountStore`, the authenticated account will automatically be persisted. Once authentication is complete, you can access the persisted `VimeoAccount` object through `VimeoClient#getVimeoAccount()`. For a basic implementation of an `AccountStore`, please refer to [this file in the example app](example/src/main/java/com/vimeo/android/networking/example/vimeonetworking/TestAccountStore.java)
```java
/**
* @param baseURLString The base url pointing to the Vimeo api. Something like: {@link Vimeo#VIMEO_BASE_URL_STRING}
* @param clientId The client id provided to you from <a href="https://developer.vimeo.com/apps/">the developer console</a>
* @param clientSecret The client secret provided to you from <a href="https://developer.vimeo.com/apps/">the developer console</a>
* @param scope Space separated list of <a href="https://developer.vimeo.com/api/authentication#scopes">scopes</a>
Expand All @@ -63,14 +67,32 @@ If you choose to pass in an `AccountStore`, the authenticated account will autom
* @param deserializer (Optional, Recommended) Extend GsonDeserializer to allow for deserialization on a background thread
*/
Configuration.Builder configBuilder =
new Configuration.Builder(Vimeo.VIMEO_BASE_URL_STRING, clientId, clientSecret, SCOPE,
new Configuration.Builder(clientId, clientSecret, SCOPE,
testAccountStore, new AndroidGsonDeserializer())
.setCacheDirectory(this.getCacheDir())
```
Setting the cache directory and deserializer are optional but highly recommended.
* The deserializer allows for deserialization on a background thread. Without it, object deserialization will occur on the main (UI) thread. This can be bad for performance if the API response bodies are large.
* The cache directory is to allow caching of requests. Without it, no requests will be cached and all other cache settings will be ignored.

#### Configuration Builder for Apps with Only One Developer Account
You can skip the need for [client credentials](#client-credentials-grant) or [code grant](#oauth-authorization-code-grant) requests if you provide an access token up front. With this access token you'll be able to make any requests that the access token's scope allows.
You will NOT be able to switch accounts if you only supply the access token. To do that, refer to the above section.

You can generate an access token in the Authentication tab once you select your app from the [list here](https://developer.vimeo.com/apps).

Once you have the access token, you can easily initialize your `VimeoClient` instance with the below builder.

```java
String accessToken = getString(R.string.access_token);
return new Configuration.Builder(accessToken);
```

After providing the access token, if you'd like to have access to the associated `User` object you'll need to make a call to `VimeoClient#fetchCurrentUser`. If you're using an account store, you can update the `VimeoAccount` with the new `User` object.

*Note: You will not be able to log out of the account associated with the access token provided to the `Configuration.Builder`. This is because we wouldn't want anyone to accidentally invalidate/delete the token which is being used to authenticate users in a production application. You will still be able to delete the token via the web [developer console](https://developer.vimeo.com/apps/).
If this seems like restricting functionality, please log an issue to the [issue tracker](https://github.com/vimeo/vimeo-networking-java/issues).

### Authentication
All calls to the Vimeo API must be [authenticated](https://developer.vimeo.com/api/authentication). This means that before making requests to the API you must authenticate and obtain an access token. Two authentication methods are provided:

Expand All @@ -81,7 +103,7 @@ All calls to the Vimeo API must be [authenticated](https://developer.vimeo.com/a
#### Client Credentials Grant
```java
// You can't make any requests to the api without an access token. This will get you a basic
// "Client Credentials" grant which will allow you to make requests
// "Client Credentials" grant which will allow you to make requests. This requires a client id and client secret.
private void authenticateWithClientCredentials() {
VimeoClient.getInstance().authorizeWithClientCredentialsGrant(new AuthCallback() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.vimeo.android.networking.example.vimeonetworking.NetworkingLogger;
import com.vimeo.android.networking.example.vimeonetworking.TestAccountStore;
import com.vimeo.networking.Configuration;
import com.vimeo.networking.Vimeo;
import com.vimeo.networking.Vimeo.LogLevel;
import com.vimeo.networking.VimeoClient;

Expand All @@ -21,38 +20,58 @@ public class TestApp extends Application {

private static final String SCOPE = "private public create edit delete interact";

private static final boolean IS_DEBUG_BUILD = false;
// Switch to true to see how access token auth works.
private static final boolean ACCESS_TOKEN_PROVIDED = false;

private static Context mContext;

@Override
public void onCreate() {
super.onCreate();

mContext = this;

AccountPreferenceManager.initializeInstance(mContext);

// <editor-fold desc="Vimeo API Library Initialization">
Configuration.Builder configBuilder;
// This check is just as for the example. In practice, you'd use one technique or the other.
if (ACCESS_TOKEN_PROVIDED) {
configBuilder = getAccessTokenBuilder();
} else {
configBuilder = getClientIdAndClientSecretBuilder();
}
if (IS_DEBUG_BUILD) {
// Disable cert pinning if debugging (so we can intercept packets)
configBuilder.enableCertPinning(false);
configBuilder.setLogLevel(LogLevel.VERBOSE);
}
VimeoClient.initialize(configBuilder.build());
// </editor-fold>
}

public Configuration.Builder getAccessTokenBuilder() {
// The values file is left out of git, so you'll have to provide your own access token
String accessToken = getString(R.string.access_token);
return new Configuration.Builder(accessToken);
}

public Configuration.Builder getClientIdAndClientSecretBuilder() {
// The values file is left out of git, so you'll have to provide your own id and secret
String clientId = getString(R.string.client_id);
String clientSecret = getString(R.string.client_secret);
String codeGrantRedirectUri = getString(R.string.deeplink_redirect_scheme) + "://" +
getString(R.string.deeplink_redirect_host);
TestAccountStore testAccountStore = new TestAccountStore(this.getApplicationContext());
Configuration.Builder configBuilder =
new Configuration.Builder(Vimeo.VIMEO_BASE_URL_STRING, clientId, clientSecret, SCOPE,
testAccountStore, new AndroidGsonDeserializer());
new Configuration.Builder(clientId, clientSecret, SCOPE, testAccountStore,
new AndroidGsonDeserializer());
configBuilder.setCacheDirectory(this.getCacheDir())
.setUserAgentString(getUserAgentString(this)).setDebugLogger(new NetworkingLogger())
// Used for oauth flow
.setCodeGrantRedirectUri(codeGrantRedirectUri);

if (/*isDebugBuild*/false) {
// Disable cert pinning if debugging (so we can intercept packets)
configBuilder.enableCertPinning(false);
configBuilder.setLogLevel(LogLevel.VERBOSE);
}
VimeoClient.initialize(configBuilder.build());
// </editor-fold>
return configBuilder;
}

public static Context getAppContext() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public class Configuration {
public String clientSecret;
public String scope;

public String accessToken;

@Nullable
private AccountStore accountStore;
public GsonDeserializer deserializer;
Expand Down Expand Up @@ -100,6 +102,7 @@ public void deleteAccount(VimeoAccount account) {
}
}

@Nullable
public VimeoAccount loadAccount() {
if (accountStore == null) {
return null;
Expand All @@ -119,6 +122,9 @@ private Configuration(Builder builder) {
this.clientID = builder.clientID;
this.clientSecret = builder.clientSecret;
this.scope = builder.scope;

this.accessToken = builder.accessToken;

this.accountStore = builder.accountStore;
this.deserializer = builder.deserializer;

Expand All @@ -142,21 +148,26 @@ private Configuration(Builder builder) {
}

private boolean isValid() {
return (this.baseURLString != null && !this.baseURLString.trim().isEmpty() &&
this.clientID != null && !this.clientID.trim().isEmpty() &&
this.clientSecret != null && !this.clientSecret.trim().isEmpty() &&
this.scope != null && !this.scope.trim().isEmpty());
return this.baseURLString != null && (!this.baseURLString.trim().isEmpty() &&
this.clientID != null && !this.clientID.trim().isEmpty() &&
this.clientSecret != null &&
!this.clientSecret.trim().isEmpty() &&
this.scope != null && !this.scope.trim().isEmpty()) ||
(this.accessToken != null && !this.accessToken.trim().isEmpty());
}

/**
* Builder used to construct the Configuration
*/
public static class Builder {

private String baseURLString;
private String baseURLString = Vimeo.VIMEO_BASE_URL_STRING;
private String clientID;
private String clientSecret;
private String scope;

private String accessToken;

private AccountStore accountStore;
private GsonDeserializer deserializer = new GsonDeserializer();

Expand All @@ -174,10 +185,34 @@ public static class Builder {
public DebugLoggerInterface debugLogger = new DebugLogger();
public LogLevel logLevel = LogLevel.DEBUG;

/**
* The most basic builder constructor. If you've only provided an access token, you'll only be able to
* make requests for the given access token's account.
* <p/>
* If you'd like the ability to switch accounts or request a client credentials grant, you'll have to set a client id and client secret.
* If you'd like the ability to persist accounts, you'll have to set an account store.
* If you'd like the ability to issue code grant, you'll have to set a code grant redirect uri.
*
* @param accessToken Token provided by the Vimeo developer console
*/
public Builder(String accessToken) {
this.accessToken = accessToken;
}

public Builder(String clientID, String clientSecret, String scope) {
this(null, clientID, clientSecret, scope, null, null);
}

@Deprecated
public Builder(String baseURLString, String clientID, String clientSecret, String scope) {
this(baseURLString, clientID, clientSecret, scope, null, null);
}

public Builder(String clientId, String clientSecret, String scope,
@Nullable AccountStore accountStore, @Nullable GsonDeserializer deserializer) {
this(null, clientId, clientSecret, scope, accountStore, deserializer);
}

/**
* The constructor for the Configuration Builder. Only the last two arguments are optional but it is
* highly recommended that you pass in a deserializer since, without one, deserialization will occur
Expand All @@ -192,16 +227,29 @@ public Builder(String baseURLString, String clientID, String clientSecret, Strin
* @param accountStore (Optional, Recommended) An implementation that can be used to interface with Androids <a href="http://developer.android.com/reference/android/accounts/AccountManager.html">Account Manager</a>
* @param deserializer (Optional, Recommended) Extend GsonDeserializer to allow for deserialization on a background thread
*/
public Builder(String baseURLString, String clientId, String clientSecret, String scope,
@Deprecated
public Builder(@Nullable String baseURLString, String clientId, String clientSecret, String scope,
@Nullable AccountStore accountStore, @Nullable GsonDeserializer deserializer) {
this.baseURLString = baseURLString;
this.baseURLString = baseURLString == null ? this.baseURLString : baseURLString;
this.clientID = clientId;
this.clientSecret = clientSecret;
this.scope = scope;
this.accountStore = accountStore;
this.deserializer = deserializer;
}

public Builder setBaseUrl(String baseUrl) {
this.baseURLString = baseUrl;
return this;
}

/** If you used the basic Builder access token constructor but have the intent of */
public Builder setClientIdAndSecret(String clientId, String clientSecret) {
this.clientID = clientId;
this.clientSecret = clientSecret;
return this;
}

public Builder setAccountStore(AccountStore accountStore) {
this.accountStore = accountStore;
return this;
Expand All @@ -212,6 +260,11 @@ public Builder setGsonDeserializer(GsonDeserializer deserializer) {
return this;
}

public Builder setAccessToken(String accessToken) {
this.accessToken = accessToken;
return this;
}

public Builder setCodeGrantRedirectUri(String redirectUri) {
this.codeGrantRedirectUri = redirectUri;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
*/
public class VimeoClient {

@NotNull
private Configuration configuration;
private VimeoService vimeoService;
@Nullable
Expand Down Expand Up @@ -195,12 +196,19 @@ public VimeoAccount getVimeoAccount() {

public void setVimeoAccount(@Nullable VimeoAccount vimeoAccount) {
if (vimeoAccount == null) {
vimeoAccount = new VimeoAccount();
vimeoAccount = new VimeoAccount(this.configuration.accessToken);
this.configuration.saveAccount(vimeoAccount, null, null);
}

this.vimeoAccount = vimeoAccount;
}

/** Sets the {@link #vimeoAccount} field as well as triggering the saveAccount event for the account store */
public void saveAccount(@Nullable VimeoAccount vimeoAccount, String email, String password) {
setVimeoAccount(vimeoAccount);
this.configuration.saveAccount(vimeoAccount, email, password);
}

public Configuration getConfiguration() {
return this.configuration;
}
Expand Down Expand Up @@ -289,9 +297,10 @@ public Call<VimeoAccount> authenticateWithCodeGrant(String uri, AuthCallback cal
}

/**
* Authorizes users of the app who are not signed in.
* Authorizes users of the app who are not signed in. This call requires a client id and client secret to be
* set on the initial Configuration.
* <p/>
* Leaves User as null in {@link VimeoAccount} model and populates the rest
* Leaves User as null in {@link VimeoAccount} model and populates the rest.
*
* @param callback Callback pertaining to authentication
*/
Expand Down Expand Up @@ -451,9 +460,7 @@ public VimeoAccount logIn(String email, String password) {
e.printStackTrace();
}

this.setVimeoAccount(vimeoAccount);

this.configuration.saveAccount(vimeoAccount, email, password);
saveAccount(vimeoAccount, email, password);

return vimeoAccount;
}
Expand Down Expand Up @@ -491,7 +498,17 @@ public Call<VimeoAccount> loginWithFacebookToken(String facebookToken, String em
* @param callback Callback for handling logout
*/
public Call<Object> logOut(@Nullable final VimeoCallback<Object> callback) {

// If you've provided an access token to the configuration builder, we're assuming that you wouldn't
// want to be able to log out of it, because this would invalidate the constant you've provided us.
if (configuration.accessToken != null &&
configuration.accessToken.equals(vimeoAccount.getAccessToken())) {
if (callback != null) {
callback.failure(new VimeoError(
"You can't log out of the account provided through the configuration builder. " +
"This is to ensure the access token generated in the developer console isn't accidentally invalidated. "));
}
return null;
}
Call<Object> call = this.vimeoService.logOut(getAuthHeader());
if (callback != null) {
callback.setCall(call);
Expand Down Expand Up @@ -562,15 +579,14 @@ public AccountCallback(VimeoClient client, String email, String password, AuthCa

@Override
public void success(VimeoAccount vimeoAccount) {
this.client.setVimeoAccount(vimeoAccount);
if (vimeoAccount.getUser() != null && (this.email == null || this.email.isEmpty())) {
// We must always have a `name` field, which is used by the Android Account Manager for
// display in the device Settings -> Accounts [KZ] 12/17/15
String name = (vimeoAccount.getUser().name !=
null) ? vimeoAccount.getUser().name : vimeoAccount.getUser().uri;
this.client.configuration.saveAccount(vimeoAccount, name, null);
this.client.saveAccount(vimeoAccount, name, null);
} else {
this.client.configuration.saveAccount(vimeoAccount, this.email, this.password);
this.client.saveAccount(vimeoAccount, this.email, this.password);
}
this.callback.success();
}
Expand Down
Loading

0 comments on commit 9161b7d

Please sign in to comment.