Skip to content

Commit

Permalink
Merge pull request #95 from auth0/add-chrome-custom-tabs
Browse files Browse the repository at this point in the history
Use Chrome Custom Tabs when possible
  • Loading branch information
lbalmaceda authored Jul 3, 2017
2 parents f11dc38 + 9ad1e0d commit e7f06e7
Show file tree
Hide file tree
Showing 14 changed files with 1,207 additions and 100 deletions.
69 changes: 42 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Android API version 15 or newer

## Installation

###Gradle
### Gradle

Auth0.android is available through [Gradle](https://gradle.org/). To install it, simply add the following line to your `build.gradle` file:

Expand Down Expand Up @@ -269,60 +269,75 @@ First go to [Auth0 Dashboard](https://manage.auth0.com/#/applications) and go to
https://{YOUR_AUTH0_DOMAIN}/android/{YOUR_APP_PACKAGE_NAME}/callback
```

Open your app's `AndroidManifest.xml` file and add the following permission.
Remember to replace `{YOUR_APP_PACKAGE_NAME}` with your actual application's package name, available in your `app/build.gradle` file as the `applicationId` value.

```xml
<uses-permission android:name="android.permission.INTERNET" />

Next, define a placeholder for the Auth0 Domain which is going to be used internally by the library to register an **intent-filter**. Go to your application's `build.gradle` file and add the `manifestPlaceholders` line as shown below:

```groovy
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
defaultConfig {
applicationId "com.auth0.samples"
minSdkVersion 15
targetSdkVersion 25
//...
//---> Add the next line
manifestPlaceholders = [auth0Domain: "@string/auth0_domain"]
//<---
}
//...
}
```

Also register the intent filters inside your activity's tag, so you can receive the call in your activity. Note that you will have to specify the callback url inside the `data` tag.
It's a good practice to define reusable resources like `@string/auth0_domain` but you can also hard code the value in the file.

Alternatively, you can declare the `RedirectActivity` in the `AndroidManifest.xml` file with your own **intent-filter** so it overrides the library's default. If you do this then the `manifestPlaceholders` don't need to be set as long as the activity contains the `tools:node="replace"` like in the snippet below. If you choose to use a [custom scheme](#a-note-about-app-deep-linking) you must define your own intent-filter as explained below.

In your manifest inside your application's tag add the `RedirectActivity` declaration:

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="your.app.package">
<application android:theme="@style/AppTheme">

<!-- ... -->

<activity
android:name="com.mycompany.MainActivity"
android:theme="@style/MyAppTheme"
android:launchMode="singleTask">

<intent-filter android:autoVerify="true">
android:name="com.auth0.android.provider.RedirectActivity"
tools:node="replace">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="{YOUR_AUTH0_DOMAIN}"
android:pathPrefix="/android/{YOUR_APP_PACKAGE_NAME}/callback"
android:host="@string/auth0_domain"
android:pathPrefix="/android/${applicationId}/callback"
android:scheme="https" />
</intent-filter>

</activity>

<!-- ... -->

</application>
</manifest>
```

Make sure the Activity's **launchMode** is declared as "singleTask" or the result won't come back after the authentication.
If you request a different scheme you must replace the `android:scheme` property value. Finally, don't forget to add the internet permission.

When you launch the WebAuthProvider you'll expect a result back. To capture the response override the `onNewIntent` method and call `WebAuthProvider.resume()` with the received parameters:
```xml
<uses-permission android:name="android.permission.INTERNET" />
```

```java
public class MyActivity extends Activity {

@Override
protected void onNewIntent(Intent intent) {
if (WebAuthProvider.resume(intent)) {
return;
}
super.onNewIntent(intent);
}
}
> In versions 1.8.0 and before you had to define the **intent-filter** inside your activity to capture the result in the `onNewIntent` method and call `WebAuthProvider.resume()` with the received intent. This call is no longer required for versions greater than 1.8.0 as it's now done for you by the library.
```

##### A note about App Deep Linking:

Expand Down Expand Up @@ -451,7 +466,7 @@ android {

ref: https://github.com/square/okio/issues/58#issuecomment-72672263

##Proguard
## Proguard
The rules should be applied automatically if your application is using `minifyEnabled = true`. If you want to include them manually check the [proguard directory](proguard).
By default you should at least use the following files:
* `proguard-okio.pro`
Expand Down
11 changes: 9 additions & 2 deletions auth0/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,21 @@ android {
lintOptions {
warning 'InvalidPackage'
}
buildTypes {
debug {
//Helps tests. buildTypes values are not included in the merged manifest
manifestPlaceholders = [auth0Domain: "auth0.test.domain"]
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:customtabs:25.3.1'
compile 'com.squareup.okhttp:okhttp:2.7.5'
compile 'com.squareup.okhttp:logging-interceptor:2.7.5'
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.google.code.gson:gson:2.7'
compile 'com.auth0.android:jwtdecode:1.1.0'

testCompile 'junit:junit:4.12'
Expand Down
33 changes: 32 additions & 1 deletion auth0/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,35 @@
~ THE SOFTWARE.
-->

<manifest package="com.auth0.android.auth0" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.auth0.android.auth0">

<uses-permission android:name="android.permission.INTERNET" />

<application>
<activity
android:name="com.auth0.android.provider.AuthenticationActivity"
android:exported="false"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />

<activity
android:name="com.auth0.android.provider.RedirectActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="${auth0Domain}"
android:pathPrefix="/android/${applicationId}/callback"
android:scheme="https" />
</intent-filter>
</activity>

<activity android:name="com.auth0.android.provider.WebAuthActivity" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.auth0.android.provider;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;

public class AuthenticationActivity extends Activity {

static final String EXTRA_USE_BROWSER = "com.auth0.android.EXTRA_USE_BROWSER";
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";
private static final String EXTRA_INTENT_LAUNCHED = "com.auth0.android.EXTRA_INTENT_LAUNCHED";

private boolean intentLaunched;
private CustomTabsController customTabsController;

static void authenticateUsingBrowser(Context context, Uri authorizeUri) {
Intent intent = new Intent(context, AuthenticationActivity.class);
intent.setData(authorizeUri);
intent.putExtra(AuthenticationActivity.EXTRA_USE_BROWSER, true);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
}

static void authenticateUsingWebView(Activity activity, Uri authorizeUri, int requestCode, String connection, boolean useFullScreen) {
Intent intent = new Intent(activity, AuthenticationActivity.class);
intent.setData(authorizeUri);
intent.putExtra(AuthenticationActivity.EXTRA_USE_BROWSER, false);
intent.putExtra(AuthenticationActivity.EXTRA_USE_FULL_SCREEN, useFullScreen);
intent.putExtra(AuthenticationActivity.EXTRA_CONNECTION_NAME, connection);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
activity.startActivityForResult(intent, requestCode);
}

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
deliverSuccessfulAuthenticationResult(data);
}
finish();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(EXTRA_INTENT_LAUNCHED, intentLaunched);
}

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
intentLaunched = savedInstanceState.getBoolean(EXTRA_INTENT_LAUNCHED, false);
}
}

@Override
protected void onResume() {
super.onResume();
if (!intentLaunched) {
intentLaunched = true;
launchAuthenticationIntent();
return;
}

if (getIntent().getData() != null) {
deliverSuccessfulAuthenticationResult(getIntent());
}
finish();
}

@Override
protected void onDestroy() {
super.onDestroy();
if (customTabsController != null) {
customTabsController.unbindService();
customTabsController = null;
}
}

private void launchAuthenticationIntent() {
Bundle extras = getIntent().getExtras();
final Uri authorizeUri = getIntent().getData();
if (!extras.getBoolean(EXTRA_USE_BROWSER, true)) {
Intent intent = new Intent(this, WebAuthActivity.class);
intent.setData(authorizeUri);
intent.putExtra(WebAuthActivity.CONNECTION_NAME_EXTRA, extras.getString(EXTRA_CONNECTION_NAME));
intent.putExtra(WebAuthActivity.FULLSCREEN_EXTRA, extras.getBoolean(EXTRA_USE_FULL_SCREEN));
//The request code value can be ignored
startActivityForResult(intent, 33);
return;
}

customTabsController = createCustomTabsController(this);
customTabsController.bindService();
customTabsController.launchUri(authorizeUri);
}

@VisibleForTesting
CustomTabsController createCustomTabsController(@NonNull Context context) {
return new CustomTabsController(context);
}

@VisibleForTesting
void deliverSuccessfulAuthenticationResult(Intent result) {
WebAuthProvider.resume(result);
}

}
Loading

0 comments on commit e7f06e7

Please sign in to comment.