Skip to content

Commit

Permalink
Merge pull request #111 from auth0/cct-show-title
Browse files Browse the repository at this point in the history
Allow Custom Tabs UI to be customizable
  • Loading branch information
lbalmaceda authored Oct 17, 2017
2 parents d7f97b3 + 5860da2 commit 2565cdb
Show file tree
Hide file tree
Showing 11 changed files with 408 additions and 14 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,22 @@ WebAuthProvider.init(account)
```


#### Customize the Custom Tabs UI

If the device where the app is running has a Custom Tabs compatible Browser, a Custom Tab will be preferred for the authentication flow. You can customize the Page Title visibility and the Toolbar color by using the `CustomTabsOptions` class.

```java
CustomTabsOptions options = CustomTabsOptions.newBuilder()
.withToolbarColor(R.color.ct_toolbar_color)
.showTitle(true)
.build();

WebAuthProvider.init(account)
.withCustomTabsOptions(options)
.start(MainActivity.this, authCallback);
```


## Next steps

### Learning resources
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,22 @@ public class AuthenticationActivity extends Activity {
static final String EXTRA_USE_FULL_SCREEN = "com.auth0.android.EXTRA_USE_FULL_SCREEN";
static final String EXTRA_CONNECTION_NAME = "com.auth0.android.EXTRA_CONNECTION_NAME";
static final String EXTRA_AUTHORIZE_URI = "com.auth0.android.EXTRA_AUTHORIZE_URI";
private static final String EXTRA_INTENT_LAUNCHED = "com.auth0.android.EXTRA_INTENT_LAUNCHED";
static final String EXTRA_INTENT_LAUNCHED = "com.auth0.android.EXTRA_INTENT_LAUNCHED";
static final String EXTRA_CT_OPTIONS = "com.auth0.android.EXTRA_CT_OPTIONS";

private boolean intentLaunched;
private CustomTabsController customTabsController;

static void authenticateUsingBrowser(Context context, Uri authorizeUri) {
static void authenticateUsingBrowser(@NonNull Context context, @NonNull Uri authorizeUri, @Nullable CustomTabsOptions options) {
Intent intent = new Intent(context, AuthenticationActivity.class);
intent.putExtra(AuthenticationActivity.EXTRA_AUTHORIZE_URI, authorizeUri);
intent.putExtra(AuthenticationActivity.EXTRA_USE_BROWSER, true);
intent.putExtra(AuthenticationActivity.EXTRA_CT_OPTIONS, options);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
}

static void authenticateUsingWebView(Activity activity, Uri authorizeUri, int requestCode, String connection, boolean useFullScreen) {
static void authenticateUsingWebView(@NonNull Activity activity, @NonNull Uri authorizeUri, int requestCode, String connection, boolean useFullScreen) {
Intent intent = new Intent(activity, AuthenticationActivity.class);
intent.putExtra(AuthenticationActivity.EXTRA_AUTHORIZE_URI, authorizeUri);
intent.putExtra(AuthenticationActivity.EXTRA_USE_BROWSER, false);
Expand Down Expand Up @@ -105,6 +107,7 @@ private void launchAuthenticationIntent() {
}

customTabsController = createCustomTabsController(this);
customTabsController.setCustomizationOptions((CustomTabsOptions) extras.getParcelable(EXTRA_CT_OPTIONS));
customTabsController.bindService();
customTabsController.launchUri(authorizeUri);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.customtabs.CustomTabsClient;
import android.support.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsServiceConnection;
import android.support.customtabs.CustomTabsSession;
import android.util.Log;
Expand Down Expand Up @@ -40,6 +40,8 @@ class CustomTabsController extends CustomTabsServiceConnection {
private final CountDownLatch sessionLatch;
private final String preferredPackage;

@Nullable
private CustomTabsOptions customTabsOptions;

@VisibleForTesting
CustomTabsController(@NonNull Context context, @NonNull String browserPackage) {
Expand All @@ -58,6 +60,15 @@ void clearContext() {
this.context.clear();
}

void setCustomizationOptions(@Nullable CustomTabsOptions options) {
this.customTabsOptions = options;
}

@VisibleForTesting
CustomTabsOptions getCustomizationOptions() {
return this.customTabsOptions;
}

@Override
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) {
if (customTabsClient == null) {
Expand Down Expand Up @@ -113,6 +124,10 @@ public void launchUri(@NonNull final Uri uri) {
return;
}

if (customTabsOptions == null) {
customTabsOptions = CustomTabsOptions.newBuilder().build();
}

new Thread(new Runnable() {
@Override
public void run() {
Expand All @@ -123,9 +138,7 @@ public void run() {
}
Log.d(TAG, "Launching URI. Custom Tabs available: " + available);

final Intent intent = new CustomTabsIntent.Builder(session.get())
.build()
.intent;
final Intent intent = customTabsOptions.toIntent(context, session.get());
intent.setData(uri);
try {
context.startActivity(intent);
Expand Down Expand Up @@ -182,5 +195,4 @@ static String getBestBrowserPackage(@NonNull Context context) {
return defaultBrowser;
}
}

}
121 changes: 121 additions & 0 deletions auth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.auth0.android.provider;

import android.content.Context;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.ColorRes;
import android.support.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsSession;
import android.support.v4.content.ContextCompat;

/**
* Holder for Custom Tabs customization options. Use {@link CustomTabsOptions#newBuilder()} to begin.
*/
public class CustomTabsOptions implements Parcelable {

private final boolean showTitle;
@ColorRes
private final int toolbarColor;

private CustomTabsOptions(boolean showTitle, @ColorRes int toolbarColor) {
this.showTitle = showTitle;
this.toolbarColor = toolbarColor;
}

/**
* Create a new CustomTabsOptions.Builder instance.
*
* @return a new CustomTabsOptions.Builder ready to customize.
*/
public static Builder newBuilder() {
return new Builder();
}


Intent toIntent(Context context, CustomTabsSession session) {
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(session)
.setShowTitle(showTitle);
if (toolbarColor > 0) {
//Resource exists
builder.setToolbarColor(ContextCompat.getColor(context, toolbarColor));
}
return builder.build().intent;
}


protected CustomTabsOptions(Parcel in) {
showTitle = in.readByte() != 0x00;
toolbarColor = in.readInt();
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (showTitle ? 0x01 : 0x00));
dest.writeInt(toolbarColor);
}

@SuppressWarnings("unused")
public static final Parcelable.Creator<CustomTabsOptions> CREATOR = new Parcelable.Creator<CustomTabsOptions>() {
@Override
public CustomTabsOptions createFromParcel(Parcel in) {
return new CustomTabsOptions(in);
}

@Override
public CustomTabsOptions[] newArray(int size) {
return new CustomTabsOptions[size];
}
};


@SuppressWarnings("WeakerAccess")
public static class Builder {
@ColorRes
private int toolbarColor;
private boolean showTitle;

private Builder() {
this.showTitle = false;
this.toolbarColor = 0;
}

/**
* Change the Custom Tab toolbar color to the given one.
*
* @param toolbarColor the new toolbar color to set
* @return this same builder instance.
*/
public Builder withToolbarColor(@ColorRes int toolbarColor) {
this.toolbarColor = toolbarColor;
return this;
}

/**
* Whether to make the Custom Tab show the Page Title in the toolbar or not.
* By default, the Page Title will be hidden.
*
* @param showTitle whether to show the Page Title in the toolbar or not.
* @return this same builder instance.
*/
public Builder showTitle(boolean showTitle) {
this.showTitle = showTitle;
return this;
}

/**
* Create a new CustomTabsOptions instance with the customization settings.
*
* @return an instance of CustomTabsOptions with the customization settings.
*/
public CustomTabsOptions build() {
return new CustomTabsOptions(showTitle, toolbarColor);
}
}

}
12 changes: 11 additions & 1 deletion auth0/src/main/java/com/auth0/android/provider/OAuthManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class OAuthManager {
private int requestCode;
private PKCE pkce;
private Long currentTimeInMillis;
private CustomTabsOptions ctOptions;

OAuthManager(@NonNull Auth0 account, @NonNull AuthCallback callback, @NonNull Map<String, String> parameters) {
this.account = account;
Expand All @@ -77,6 +78,10 @@ void useBrowser(boolean useBrowser) {
this.useBrowser = useBrowser;
}

public void setCustomTabsOptions(@Nullable CustomTabsOptions options) {
this.ctOptions = options;
}

@VisibleForTesting
void setPKCE(PKCE pkce) {
this.pkce = pkce;
Expand All @@ -90,7 +95,7 @@ void startAuthorization(Activity activity, String redirectUri, int requestCode)
this.requestCode = requestCode;

if (useBrowser) {
AuthenticationActivity.authenticateUsingBrowser(activity, uri);
AuthenticationActivity.authenticateUsingBrowser(activity, uri, ctOptions);
} else {
AuthenticationActivity.authenticateUsingWebView(activity, uri, requestCode, parameters.get(KEY_CONNECTION), useFullScreen);
}
Expand Down Expand Up @@ -259,6 +264,11 @@ boolean useFullScreen() {
return useFullScreen;
}

@VisibleForTesting
CustomTabsOptions customTabsOptions() {
return ctOptions;
}

@VisibleForTesting
static Credentials mergeCredentials(Credentials urlCredentials, Credentials codeCredentials) {
final String idToken = TextUtils.isEmpty(codeCredentials.getIdToken()) ? urlCredentials.getIdToken() : codeCredentials.getIdToken();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public static class Builder {
private boolean useFullscreen;
private PKCE pkce;
private String scheme;
private CustomTabsOptions ctOptions;

Builder(Auth0 account) {
this.account = account;
Expand Down Expand Up @@ -249,6 +250,17 @@ public Builder withConnection(@NonNull String connectionName) {
return this;
}

/**
* When using a Custom Tabs compatible Browser, apply this customization options.
*
* @param options the Custom Tabs customization options
* @return the current builder instance
*/
public Builder withCustomTabsOptions(@NonNull CustomTabsOptions options) {
this.ctOptions = options;
return this;
}

@VisibleForTesting
Builder withPKCE(PKCE pkce) {
this.pkce = pkce;
Expand All @@ -275,6 +287,7 @@ public void start(@NonNull Activity activity, @NonNull AuthCallback callback, in
OAuthManager manager = new OAuthManager(account, callback, values);
manager.useFullScreen(useFullscreen);
manager.useBrowser(useBrowser);
manager.setCustomTabsOptions(ctOptions);
manager.setPKCE(pkce);

managerInstance = manager;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public class AuthenticationActivityTest {
private Uri resultUri;
@Mock
private CustomTabsController customTabsController;
@Mock
private CustomTabsOptions customTabsOptions;
@Captor
private ArgumentCaptor<Intent> intentCaptor;
@Captor
Expand All @@ -71,7 +73,7 @@ private void createActivity(Intent configurationIntent) {
@SuppressWarnings("deprecation")
@Test
public void shouldAuthenticateUsingBrowser() throws Exception {
AuthenticationActivity.authenticateUsingBrowser(callerActivity, uri);
AuthenticationActivity.authenticateUsingBrowser(callerActivity, uri, customTabsOptions);
verify(callerActivity).startActivity(intentCaptor.capture());

createActivity(intentCaptor.getValue());
Expand Down Expand Up @@ -102,7 +104,7 @@ public void shouldAuthenticateUsingBrowser() throws Exception {
@SuppressWarnings("deprecation")
@Test
public void shouldAuthenticateAfterRecreatedUsingBrowser() throws Exception {
AuthenticationActivity.authenticateUsingBrowser(callerActivity, uri);
AuthenticationActivity.authenticateUsingBrowser(callerActivity, uri, customTabsOptions);
verify(callerActivity).startActivity(intentCaptor.capture());

createActivity(intentCaptor.getValue());
Expand Down Expand Up @@ -131,7 +133,7 @@ public void shouldAuthenticateAfterRecreatedUsingBrowser() throws Exception {
@SuppressWarnings("deprecation")
@Test
public void shouldCancelAuthenticationUsingBrowser() throws Exception {
AuthenticationActivity.authenticateUsingBrowser(callerActivity, uri);
AuthenticationActivity.authenticateUsingBrowser(callerActivity, uri, customTabsOptions);
verify(callerActivity).startActivity(intentCaptor.capture());

createActivity(intentCaptor.getValue());
Expand Down Expand Up @@ -263,7 +265,7 @@ public void shouldCancelAuthenticationUsingWebView() throws Exception {
@SuppressWarnings("deprecation")
@Test
public void shouldLaunchForBrowserAuthentication() throws Exception {
AuthenticationActivity.authenticateUsingBrowser(callerActivity, uri);
AuthenticationActivity.authenticateUsingBrowser(callerActivity, uri, customTabsOptions);
verify(callerActivity).startActivity(intentCaptor.capture());

Intent intent = intentCaptor.getValue();
Expand All @@ -278,6 +280,7 @@ public void shouldLaunchForBrowserAuthentication() throws Exception {
Assert.assertThat(extras.containsKey(AuthenticationActivity.EXTRA_USE_FULL_SCREEN), is(false));
Assert.assertThat(extras.containsKey(AuthenticationActivity.EXTRA_USE_BROWSER), is(true));
Assert.assertThat(extras.getBoolean(AuthenticationActivity.EXTRA_USE_BROWSER), is(true));
Assert.assertThat((CustomTabsOptions) extras.getParcelable(AuthenticationActivity.EXTRA_CT_OPTIONS), is(customTabsOptions));
}

@SuppressWarnings("deprecation")
Expand All @@ -300,6 +303,7 @@ public void shouldLaunchForWebViewAuthentication() throws Exception {
Assert.assertThat(extras.getBoolean(AuthenticationActivity.EXTRA_USE_FULL_SCREEN), is(true));
Assert.assertThat(extras.containsKey(AuthenticationActivity.EXTRA_USE_BROWSER), is(true));
Assert.assertThat(extras.getBoolean(AuthenticationActivity.EXTRA_USE_BROWSER), is(false));
Assert.assertThat(extras.getBoolean(AuthenticationActivity.EXTRA_CT_OPTIONS), is(false));
}

@Test
Expand Down
Loading

0 comments on commit 2565cdb

Please sign in to comment.