java.lang.Object
↳Service
↳androidx.browser.customtabs.CustomTabsService
Gradle dependencies
compile group: 'androidx.browser', name: 'browser', version: '1.8.0'
- groupId: androidx.browser
- artifactId: browser
- version: 1.8.0
Artifact androidx.browser:browser:1.8.0 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.browser:browser com.android.support:customtabs
Androidx class mapping:
androidx.browser.customtabs.CustomTabsService android.support.customtabs.CustomTabsService
Overview
Abstract service class for implementing Custom Tabs related functionality. The service should
be responding to the action ACTION_CUSTOM_TABS_CONNECTION. This class should be used by
implementers that want to provide Custom Tabs functionality, not by clients that want to launch
Custom Tabs.
Summary
Fields |
---|
public static final java.lang.String | ACTION_CUSTOM_TABS_CONNECTION The Intent action that a CustomTabsService must respond to. |
public static final java.lang.String | CATEGORY_COLOR_SCHEME_CUSTOMIZATION An Intent filter category to signify that the Custom Tabs provider supports selecting and
customizing color schemes via CustomTabsIntent.Builder.setColorScheme(int) and
CustomTabsIntent.Builder.setColorSchemeParams(int, CustomTabColorSchemeParams). |
public static final java.lang.String | CATEGORY_NAVBAR_COLOR_CUSTOMIZATION An Intent filter category to signify that the Custom Tabs provider supports customizing
the color of the navigation bar (CustomTabsIntent.Builder.setNavigationBarColor(int)). |
public static final java.lang.String | CATEGORY_TRUSTED_WEB_ACTIVITY_IMMERSIVE_MODE An Intent filter category to signify that the Trusted Web Activity provider supports
immersive mode. |
public static final java.lang.String | CATEGORY_WEB_SHARE_TARGET_V2 An Intent filter category to signify that the Trusted Web Activity provider supports
sending shared data according to the Web Share Target v2 protocol defined in
https://wicg.github.io/web-share-target/level-2/. |
public static final int | FILE_PURPOSE_TRUSTED_WEB_ACTIVITY_SPLASH_IMAGE A constant to be used with CustomTabsSession.receiveFile(Uri, int, Bundle) indicating that the file
is a splash image to be shown on top of a Trusted Web Activity while the web contents
are loading. |
public static final java.lang.String | KEY_SUCCESS The key to use to store a boolean in the returns bundle of CustomTabsService.extraCommand(String, Bundle) method,
to indicate the command is executed successfully. |
public static final java.lang.String | KEY_URL For CustomTabsService.mayLaunchUrl(CustomTabsSessionToken, Uri, Bundle, List) calls that wants to specify more than one url,
this key can be used with
to insert a new url to each bundle inside list of bundles. |
public static final int | RELATION_HANDLE_ALL_URLS Used for CustomTabsSession.validateRelationship(int, Uri, Bundle). |
public static final int | RELATION_USE_AS_ORIGIN Used for CustomTabsSession.validateRelationship(int, Uri, Bundle). |
public static final int | RESULT_FAILURE_DISALLOWED Indicates that the postMessage request was not allowed due to a bad argument or requesting
at a disallowed time like when in background. |
public static final int | RESULT_FAILURE_MESSAGING_ERROR Indicates that the postMessage request has failed due to an internal error on the browser
message channel. |
public static final int | RESULT_FAILURE_REMOTE_ERROR Indicates that the postMessage request has failed due to a . |
public static final int | RESULT_SUCCESS Indicates that the postMessage request was accepted. |
public static final java.lang.String | TRUSTED_WEB_ACTIVITY_CATEGORY An Intent filter category to signify that the Custom Tabs provider supports Trusted Web
Activities (see TrustedWebUtils for more details). |
Methods |
---|
protected boolean | cleanUpSession(CustomTabsSessionToken sessionToken)
Called when the client side IBinder for this CustomTabsSessionToken is dead. |
protected abstract Bundle | extraCommand(java.lang.String commandName, Bundle args)
Unsupported commands that may be provided by the implementation. |
protected boolean | isEngagementSignalsApiAvailable(CustomTabsSessionToken sessionToken, Bundle extras)
Returns whether the Engagement Signals API is available. |
protected abstract boolean | mayLaunchUrl(CustomTabsSessionToken sessionToken, Uri url, Bundle extras, java.util.List<Bundle> otherLikelyBundles)
Tells the browser of a likely future navigation to a URL. |
protected abstract boolean | newSession(CustomTabsSessionToken sessionToken)
Creates a new session through an ICustomTabsService with the optional callback. |
public IBinder | onBind(Intent intent)
|
protected abstract int | postMessage(CustomTabsSessionToken sessionToken, java.lang.String message, Bundle extras)
Sends a postMessage request using the origin communicated via
CustomTabsService.requestPostMessageChannel(CustomTabsSessionToken, Uri). |
protected abstract boolean | receiveFile(CustomTabsSessionToken sessionToken, Uri uri, int purpose, Bundle extras)
Receive a file from client by given Uri, e.g. |
protected abstract boolean | requestPostMessageChannel(CustomTabsSessionToken sessionToken, Uri postMessageOrigin)
Sends a request to create a two way postMessage channel between the client and the browser
linked with the given CustomTabsSession. |
protected boolean | requestPostMessageChannel(CustomTabsSessionToken sessionToken, Uri postMessageOrigin, Uri postMessageTargetOrigin, Bundle extras)
Same as above method with specifying the target origin to establish communication with. |
protected boolean | setEngagementSignalsCallback(CustomTabsSessionToken sessionToken, EngagementSignalsCallback callback, Bundle extras)
Sets an EngagementSignalsCallback to execute callbacks for events related to
the user's engagement with the webpage within the tab. |
protected abstract boolean | updateVisuals(CustomTabsSessionToken sessionToken, Bundle bundle)
Updates the visuals of custom tabs for the given session. |
protected abstract boolean | validateRelationship(CustomTabsSessionToken sessionToken, int relation, Uri origin, Bundle extras)
Request to validate a relationship between the application and an origin. |
protected abstract boolean | warmup(long flags)
Warms up the browser process asynchronously. |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final java.lang.String
ACTION_CUSTOM_TABS_CONNECTIONThe Intent action that a CustomTabsService must respond to.
public static final java.lang.String
CATEGORY_NAVBAR_COLOR_CUSTOMIZATIONAn Intent filter category to signify that the Custom Tabs provider supports customizing
the color of the navigation bar (CustomTabsIntent.Builder.setNavigationBarColor(int)).
public static final java.lang.String
CATEGORY_COLOR_SCHEME_CUSTOMIZATIONAn Intent filter category to signify that the Custom Tabs provider supports selecting and
customizing color schemes via CustomTabsIntent.Builder.setColorScheme(int) and
CustomTabsIntent.Builder.setColorSchemeParams(int, CustomTabColorSchemeParams).
public static final java.lang.String
TRUSTED_WEB_ACTIVITY_CATEGORYAn Intent filter category to signify that the Custom Tabs provider supports Trusted Web
Activities (see TrustedWebUtils for more details).
public static final java.lang.String
CATEGORY_WEB_SHARE_TARGET_V2An Intent filter category to signify that the Trusted Web Activity provider supports
sending shared data according to the Web Share Target v2 protocol defined in
https://wicg.github.io/web-share-target/level-2/.
public static final java.lang.String
CATEGORY_TRUSTED_WEB_ACTIVITY_IMMERSIVE_MODEAn Intent filter category to signify that the Trusted Web Activity provider supports
immersive mode.
public static final java.lang.String
KEY_URLFor CustomTabsService.mayLaunchUrl(CustomTabsSessionToken, Uri, Bundle, List) calls that wants to specify more than one url,
this key can be used with
to insert a new url to each bundle inside list of bundles.
public static final java.lang.String
KEY_SUCCESSThe key to use to store a boolean in the returns bundle of CustomTabsService.extraCommand(String, Bundle) method,
to indicate the command is executed successfully.
public static final int
RESULT_SUCCESSIndicates that the postMessage request was accepted.
public static final int
RESULT_FAILURE_DISALLOWEDIndicates that the postMessage request was not allowed due to a bad argument or requesting
at a disallowed time like when in background.
public static final int
RESULT_FAILURE_REMOTE_ERRORIndicates that the postMessage request has failed due to a .
public static final int
RESULT_FAILURE_MESSAGING_ERRORIndicates that the postMessage request has failed due to an internal error on the browser
message channel.
public static final int
RELATION_USE_AS_ORIGINUsed for CustomTabsSession.validateRelationship(int, Uri, Bundle). For
App -> Web transitions, requests the app to use the declared origin to be used as origin for
the client app in the web APIs context.
public static final int
RELATION_HANDLE_ALL_URLSUsed for CustomTabsSession.validateRelationship(int, Uri, Bundle). Requests the
ability to handle all URLs from a given origin.
public static final int
FILE_PURPOSE_TRUSTED_WEB_ACTIVITY_SPLASH_IMAGEA constant to be used with CustomTabsSession.receiveFile(Uri, int, Bundle) indicating that the file
is a splash image to be shown on top of a Trusted Web Activity while the web contents
are loading.
Constructors
public
CustomTabsService()
Methods
public IBinder
onBind(Intent intent)
Called when the client side IBinder
for this CustomTabsSessionToken is dead.
Can also be used to clean up instances allocated for the given token.
Parameters:
sessionToken: The session token for which the call has been
received.
Returns:
Whether the clean up was successful. Multiple calls with two tokens holdings the
same binder will return false.
protected abstract boolean
warmup(long flags)
Warms up the browser process asynchronously.
Parameters:
flags: Reserved for future use.
Returns:
Whether warmup was/had been completed successfully. Multiple successful
calls will return true.
Creates a new session through an ICustomTabsService with the optional callback. This session
can be used to associate any related communication through the service with an intent and
then later with a Custom Tab. The client can then send later service calls or intents to
through same session-intent-Custom Tab association.
Parameters:
sessionToken: Session token to be used as a unique identifier. This also has access
to the CustomTabsCallback passed from the client side through
CustomTabsSessionToken.getCallback().
Returns:
Whether a new session was successfully created.
protected abstract boolean
mayLaunchUrl(
CustomTabsSessionToken sessionToken, Uri url, Bundle extras, java.util.List<Bundle> otherLikelyBundles)
Tells the browser of a likely future navigation to a URL.
The method CustomTabsService.warmup(long) has to be called beforehand.
The most likely URL has to be specified explicitly. Optionally, a list of
other likely URLs can be provided. They are treated as less likely than
the first one, and have to be sorted in decreasing priority order. These
additional URLs may be ignored.
All previous calls to this method will be deprioritized.
Parameters:
sessionToken: The unique identifier for the session. Can not be null.
url: Most likely URL.
extras: Reserved for future use.
otherLikelyBundles: Other likely destinations, sorted in decreasing
likelihood order. Each Bundle has to provide a url.
Returns:
Whether the call was successful.
protected abstract Bundle
extraCommand(java.lang.String commandName, Bundle args)
Unsupported commands that may be provided by the implementation.
Note:Clients should never rely on this method to have a
defined behavior, as it is entirely implementation-defined and not supported.
This call can be used by implementations to add extra commands, for testing or
experimental purposes.
A return value of null will be used to signify that the client does not know how to
handle the request.
As optional best practices, CustomTabsService.KEY_SUCCESS could be use to identify
that command was *successfully* handled. For example, when returning a message with result:
Bundle result = new Bundle();
result.putString("message", message);
if (success)
result.putBoolean(KEY_SUCCESS, true);
return result;
The caller side:
Bundle result = service.extraCommand(commandName, args);
if (result.getBoolean(service.KEY_SUCCESS)) {
// Command was successfully handled
}
Parameters:
commandName: Name of the extra command to execute.
args: Arguments for the command
Returns:
The result , or null.
Updates the visuals of custom tabs for the given session. Will only succeed if the given
session matches the currently active one.
Parameters:
sessionToken: The currently active session that the custom tab belongs to.
bundle: The action button configuration bundle. This bundle should be constructed
with the same structure in CustomTabsIntent.Builder.
Returns:
Whether the operation was successful.
protected abstract boolean
requestPostMessageChannel(
CustomTabsSessionToken sessionToken, Uri postMessageOrigin)
Sends a request to create a two way postMessage channel between the client and the browser
linked with the given CustomTabsSession.
Parameters:
sessionToken: The unique identifier for the session. Can not be null.
postMessageOrigin: A origin that the client is requesting to be identified as
during the postMessage communication.
Returns:
Whether the implementation accepted the request. Note that returning true
here doesn't mean an origin has already been assigned as the validation is
asynchronous.
protected boolean
requestPostMessageChannel(
CustomTabsSessionToken sessionToken, Uri postMessageOrigin, Uri postMessageTargetOrigin, Bundle extras)
Same as above method with specifying the target origin to establish communication with.
Parameters:
sessionToken: The unique identifier for the session. Can not be null.
postMessageOrigin: A origin that the client is requesting to be identified as
during the postMessage communication.
postMessageTargetOrigin: The target Origin to establish PostMessageChannel with and
send messages to.
extras: Reserved for future use.
Returns:
Whether the implementation accepted the request. Note that returning true
here doesn't mean an origin has already been assigned as the validation is
asynchronous.
protected abstract int
postMessage(
CustomTabsSessionToken sessionToken, java.lang.String message, Bundle extras)
Sends a postMessage request using the origin communicated via
CustomTabsService.requestPostMessageChannel(CustomTabsSessionToken, Uri). Fails when called before
PostMessageServiceConnection.notifyMessageChannelReady(Bundle) is received on the
client side.
Parameters:
sessionToken: The unique identifier for the session. Can not be null.
message: The message that is being sent.
extras: Reserved for future use.
Returns:
An integer constant about the postMessage request result. Will return
CustomTabsService.RESULT_SUCCESS if successful.
protected abstract boolean
validateRelationship(
CustomTabsSessionToken sessionToken, int relation, Uri origin, Bundle extras)
Request to validate a relationship between the application and an origin.
If this method returns true, the validation result will be provided through
CustomTabsCallback.onRelationshipValidationResult(int, Uri, boolean, Bundle).
Otherwise the request didn't succeed. The client must call
CustomTabsClient.warmup(long) before this.
Parameters:
sessionToken: The unique identifier for the session. Can not be null.
relation: Relation to check, must be one of the CustomTabsService#RELATION_*
constants.
origin: Origin for the relation query.
extras: Reserved for future use.
Returns:
true if the request has been submitted successfully.
protected abstract boolean
receiveFile(
CustomTabsSessionToken sessionToken, Uri uri, int purpose, Bundle extras)
Receive a file from client by given Uri, e.g. in order to display a large bitmap in a Custom
Tab.
Prior to calling this method, the client grants a read permission to the target
Custom Tabs provider via .
The file is read and processed (where applicable) synchronously.
Parameters:
sessionToken: The unique identifier for the session.
uri: of the file.
purpose: Purpose of transferring this file, one of the constants enumerated in
CustomTabsService#FilePurpose.
extras: Reserved for future use.
Returns:
true if the file was received successfully.
Returns whether the Engagement Signals API is available. The availability of the Engagement
Signals API may change at runtime. If an EngagementSignalsCallback has been set, an
EngagementSignalsCallback.onSessionEnded(boolean, Bundle) signal will be sent if the API becomes
unavailable later.
Parameters:
sessionToken: The unique identifier for the session.
extras: Reserved for future use.
Returns:
Whether the Engagement Signals API is available. A false value means
CustomTabsService.setEngagementSignalsCallback(CustomTabsSessionToken, EngagementSignalsCallback, Bundle) will return false and not set the callback.
Sets an EngagementSignalsCallback to execute callbacks for events related to
the user's engagement with the webpage within the tab.
Parameters:
sessionToken: The unique identifier for the session.
callback: The EngagementSignalsCallback to execute the callbacks.
extras: Reserved for future use.
Returns:
Whether the callback connection is allowed. If false, no callbacks will be called for
this session.
Source
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.browser.customtabs;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.support.customtabs.ICustomTabsCallback;
import android.support.customtabs.ICustomTabsService;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.collection.SimpleArrayMap;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Abstract service class for implementing Custom Tabs related functionality. The service should
* be responding to the action ACTION_CUSTOM_TABS_CONNECTION. This class should be used by
* implementers that want to provide Custom Tabs functionality, not by clients that want to launch
* Custom Tabs.
*/
public abstract class CustomTabsService extends Service {
/**
* The Intent action that a CustomTabsService must respond to.
*/
public static final String ACTION_CUSTOM_TABS_CONNECTION =
"android.support.customtabs.action.CustomTabsService";
/**
* An Intent filter category to signify that the Custom Tabs provider supports customizing
* the color of the navigation bar ({@link CustomTabsIntent.Builder#setNavigationBarColor}).
*/
public static final String CATEGORY_NAVBAR_COLOR_CUSTOMIZATION =
"androidx.browser.customtabs.category.NavBarColorCustomization";
/**
* An Intent filter category to signify that the Custom Tabs provider supports selecting and
* customizing color schemes via {@link CustomTabsIntent.Builder#setColorScheme} and
* {@link CustomTabsIntent.Builder#setColorSchemeParams}.
*/
public static final String CATEGORY_COLOR_SCHEME_CUSTOMIZATION =
"androidx.browser.customtabs.category.ColorSchemeCustomization";
/**
* An Intent filter category to signify that the Custom Tabs provider supports Trusted Web
* Activities (see {@link TrustedWebUtils} for more details).
*/
public static final String TRUSTED_WEB_ACTIVITY_CATEGORY =
"androidx.browser.trusted.category.TrustedWebActivities";
/**
* An Intent filter category to signify that the Trusted Web Activity provider supports
* sending shared data according to the Web Share Target v2 protocol defined in
* https://wicg.github.io/web-share-target/level-2/.
*/
public static final String CATEGORY_WEB_SHARE_TARGET_V2 =
"androidx.browser.trusted.category.WebShareTargetV2";
/**
* An Intent filter category to signify that the Trusted Web Activity provider supports
* immersive mode.
*/
public static final String CATEGORY_TRUSTED_WEB_ACTIVITY_IMMERSIVE_MODE =
"androidx.browser.trusted.category.ImmersiveMode";
/**
* For {@link CustomTabsService#mayLaunchUrl} calls that wants to specify more than one url,
* this key can be used with {@link Bundle#putParcelable(String, android.os.Parcelable)}
* to insert a new url to each bundle inside list of bundles.
*/
public static final String KEY_URL =
"android.support.customtabs.otherurls.URL";
/**
* The key to use to store a boolean in the returns bundle of {@link #extraCommand} method,
* to indicate the command is executed successfully.
*/
public static final String KEY_SUCCESS = "androidx.browser.customtabs.SUCCESS";
@Retention(RetentionPolicy.SOURCE)
@IntDef({RESULT_SUCCESS, RESULT_FAILURE_DISALLOWED,
RESULT_FAILURE_REMOTE_ERROR, RESULT_FAILURE_MESSAGING_ERROR})
public @interface Result {
}
/**
* Indicates that the postMessage request was accepted.
*/
public static final int RESULT_SUCCESS = 0;
/**
* Indicates that the postMessage request was not allowed due to a bad argument or requesting
* at a disallowed time like when in background.
*/
public static final int RESULT_FAILURE_DISALLOWED = -1;
/**
* Indicates that the postMessage request has failed due to a {@link RemoteException} .
*/
public static final int RESULT_FAILURE_REMOTE_ERROR = -2;
/**
* Indicates that the postMessage request has failed due to an internal error on the browser
* message channel.
*/
public static final int RESULT_FAILURE_MESSAGING_ERROR = -3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({RELATION_USE_AS_ORIGIN, RELATION_HANDLE_ALL_URLS})
public @interface Relation {
}
/**
* Used for {@link CustomTabsSession#validateRelationship(int, Uri, Bundle)}. For
* App -> Web transitions, requests the app to use the declared origin to be used as origin for
* the client app in the web APIs context.
*/
public static final int RELATION_USE_AS_ORIGIN = 1;
/**
* Used for {@link CustomTabsSession#validateRelationship(int, Uri, Bundle)}. Requests the
* ability to handle all URLs from a given origin.
*/
public static final int RELATION_HANDLE_ALL_URLS = 2;
/**
* Enumerates the possible purposes of files received in {@link #receiveFile}.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
@Retention(RetentionPolicy.SOURCE)
@IntDef({FILE_PURPOSE_TRUSTED_WEB_ACTIVITY_SPLASH_IMAGE})
public @interface FilePurpose {
}
/**
* A constant to be used with {@link CustomTabsSession#receiveFile} indicating that the file
* is a splash image to be shown on top of a Trusted Web Activity while the web contents
* are loading.
*/
public static final int FILE_PURPOSE_TRUSTED_WEB_ACTIVITY_SPLASH_IMAGE = 1;
private static final String TAG = "CustomTabsService";
final SimpleArrayMap<IBinder, DeathRecipient> mDeathRecipientMap = new SimpleArrayMap<>();
private ICustomTabsService.Stub mBinder = new ICustomTabsService.Stub() {
@Override
public boolean warmup(long flags) {
return CustomTabsService.this.warmup(flags);
}
@Override
public boolean newSession(@NonNull ICustomTabsCallback callback) {
return newSessionInternal(callback, null);
}
@Override
public boolean newSessionWithExtras(@NonNull ICustomTabsCallback callback,
@Nullable Bundle extras) {
return newSessionInternal(callback, getSessionIdFromBundle(extras));
}
private boolean newSessionInternal(@NonNull ICustomTabsCallback callback,
@Nullable PendingIntent sessionId) {
final CustomTabsSessionToken sessionToken =
new CustomTabsSessionToken(callback, sessionId);
try {
DeathRecipient deathRecipient = () -> cleanUpSession(sessionToken);
synchronized (mDeathRecipientMap) {
callback.asBinder().linkToDeath(deathRecipient, 0);
mDeathRecipientMap.put(callback.asBinder(), deathRecipient);
}
return CustomTabsService.this.newSession(sessionToken);
} catch (RemoteException e) {
return false;
}
}
@Override
public boolean mayLaunchUrl(@Nullable ICustomTabsCallback callback, @Nullable Uri url,
@Nullable Bundle extras, @Nullable List<Bundle> otherLikelyBundles) {
return CustomTabsService.this.mayLaunchUrl(
new CustomTabsSessionToken(callback, getSessionIdFromBundle(extras)),
url, extras, otherLikelyBundles);
}
@SuppressWarnings("NullAway") // TODO: b/142938599
@Override
public Bundle extraCommand(@NonNull String commandName, @Nullable Bundle args) {
return CustomTabsService.this.extraCommand(commandName, args);
}
@Override
public boolean updateVisuals(@NonNull ICustomTabsCallback callback,
@Nullable Bundle bundle) {
return CustomTabsService.this.updateVisuals(
new CustomTabsSessionToken(callback, getSessionIdFromBundle(bundle)), bundle);
}
@Override
public boolean requestPostMessageChannel(@NonNull ICustomTabsCallback callback,
@NonNull Uri postMessageOrigin) {
return CustomTabsService.this.requestPostMessageChannel(
new CustomTabsSessionToken(callback, null), postMessageOrigin,
null, new Bundle());
}
@Override
public boolean requestPostMessageChannelWithExtras(@NonNull ICustomTabsCallback callback,
@NonNull Uri postMessageOrigin, @NonNull Bundle extras) {
return CustomTabsService.this.requestPostMessageChannel(
new CustomTabsSessionToken(callback, getSessionIdFromBundle(extras)),
postMessageOrigin, getTargetOriginFromBundle(extras), extras);
}
@Override
public int postMessage(@NonNull ICustomTabsCallback callback, @NonNull String message,
@Nullable Bundle extras) {
return CustomTabsService.this.postMessage(
new CustomTabsSessionToken(callback, getSessionIdFromBundle(extras)),
message, extras);
}
@Override
public boolean validateRelationship(
@NonNull ICustomTabsCallback callback, @Relation int relation,
@NonNull Uri origin, @Nullable Bundle extras) {
return CustomTabsService.this.validateRelationship(
new CustomTabsSessionToken(callback, getSessionIdFromBundle(extras)),
relation, origin, extras);
}
@Override
public boolean receiveFile(@NonNull ICustomTabsCallback callback, @NonNull Uri uri,
@FilePurpose int purpose, @Nullable Bundle extras) {
return CustomTabsService.this.receiveFile(
new CustomTabsSessionToken(callback, getSessionIdFromBundle(extras)),
uri, purpose, extras);
}
@Override
public boolean isEngagementSignalsApiAvailable(ICustomTabsCallback customTabsCallback,
@NonNull Bundle extras) {
return CustomTabsService.this.isEngagementSignalsApiAvailable(
new CustomTabsSessionToken(customTabsCallback, getSessionIdFromBundle(extras)),
extras);
}
@Override
public boolean setEngagementSignalsCallback(
@NonNull ICustomTabsCallback customTabsCallback, @NonNull IBinder callback,
@NonNull Bundle extras) {
EngagementSignalsCallback remote = EngagementSignalsCallbackRemote.fromBinder(
callback);
return CustomTabsService.this.setEngagementSignalsCallback(
new CustomTabsSessionToken(customTabsCallback, getSessionIdFromBundle(extras)),
remote, extras);
}
@SuppressWarnings("deprecation")
private @Nullable PendingIntent getSessionIdFromBundle(@Nullable Bundle bundle) {
if (bundle == null) return null;
PendingIntent sessionId = bundle.getParcelable(CustomTabsIntent.EXTRA_SESSION_ID);
bundle.remove(CustomTabsIntent.EXTRA_SESSION_ID);
return sessionId;
}
@SuppressWarnings("deprecation")
private @Nullable Uri getTargetOriginFromBundle(@Nullable Bundle bundle) {
if (bundle == null) return null;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return Api33Impl.getParcelable(bundle, CustomTabsSession.TARGET_ORIGIN_KEY,
Uri.class);
} else {
return bundle.getParcelable(CustomTabsSession.TARGET_ORIGIN_KEY);
}
}
};
@Override
@NonNull
public IBinder onBind(@Nullable Intent intent) {
return mBinder;
}
/**
* Called when the client side {@link IBinder} for this {@link CustomTabsSessionToken} is dead.
* Can also be used to clean up {@link DeathRecipient} instances allocated for the given token.
*
* @param sessionToken The session token for which the {@link DeathRecipient} call has been
* received.
* @return Whether the clean up was successful. Multiple calls with two tokens holdings the
* same binder will return false.
*/
protected boolean cleanUpSession(@NonNull CustomTabsSessionToken sessionToken) {
try {
synchronized (mDeathRecipientMap) {
IBinder binder = sessionToken.getCallbackBinder();
if (binder == null) return false;
DeathRecipient deathRecipient = mDeathRecipientMap.get(binder);
binder.unlinkToDeath(deathRecipient, 0);
mDeathRecipientMap.remove(binder);
}
} catch (NoSuchElementException e) {
return false;
}
return true;
}
/**
* Warms up the browser process asynchronously.
*
* @param flags Reserved for future use.
* @return Whether warmup was/had been completed successfully. Multiple successful
* calls will return true.
*/
protected abstract boolean warmup(long flags);
/**
* Creates a new session through an ICustomTabsService with the optional callback. This session
* can be used to associate any related communication through the service with an intent and
* then later with a Custom Tab. The client can then send later service calls or intents to
* through same session-intent-Custom Tab association.
*
* @param sessionToken Session token to be used as a unique identifier. This also has access
* to the {@link CustomTabsCallback} passed from the client side through
* {@link CustomTabsSessionToken#getCallback()}.
* @return Whether a new session was successfully created.
*/
protected abstract boolean newSession(@NonNull CustomTabsSessionToken sessionToken);
/**
* Tells the browser of a likely future navigation to a URL.
* <p>
* The method {@link CustomTabsService#warmup(long)} has to be called beforehand.
* The most likely URL has to be specified explicitly. Optionally, a list of
* other likely URLs can be provided. They are treated as less likely than
* the first one, and have to be sorted in decreasing priority order. These
* additional URLs may be ignored.
* All previous calls to this method will be deprioritized.
*
* @param sessionToken The unique identifier for the session. Can not be null.
* @param url Most likely URL.
* @param extras Reserved for future use.
* @param otherLikelyBundles Other likely destinations, sorted in decreasing
* likelihood order. Each Bundle has to provide a url.
* @return Whether the call was successful.
*/
protected abstract boolean mayLaunchUrl(@NonNull CustomTabsSessionToken sessionToken,
@Nullable Uri url, @Nullable Bundle extras, @Nullable List<Bundle> otherLikelyBundles);
/**
* Unsupported commands that may be provided by the implementation.
* <p>
* <p>
* <strong>Note:</strong>Clients should <strong>never</strong> rely on this method to have a
* defined behavior, as it is entirely implementation-defined and not supported.
* <p>
* <p> This call can be used by implementations to add extra commands, for testing or
* experimental purposes.
*
* A return value of {@code null} will be used to signify that the client does not know how to
* handle the request.
*
* As optional best practices, {@link #KEY_SUCCESS} could be use to identify
* that command was *successfully* handled. For example, when returning a message with result:
* <pre><code>
* Bundle result = new Bundle();
* result.putString("message", message);
* if (success)
* result.putBoolean(KEY_SUCCESS, true);
* return result;
* </code></pre>
* The caller side:
* <pre><code>
* Bundle result = service.extraCommand(commandName, args);
* if (result.getBoolean(service.KEY_SUCCESS)) {
* // Command was successfully handled
* }
* </code></pre>
*
* @param commandName Name of the extra command to execute.
* @param args Arguments for the command
* @return The result {@link Bundle}, or {@code null}.
*/
@Nullable
protected abstract Bundle extraCommand(@NonNull String commandName, @Nullable Bundle args);
/**
* Updates the visuals of custom tabs for the given session. Will only succeed if the given
* session matches the currently active one.
*
* @param sessionToken The currently active session that the custom tab belongs to.
* @param bundle The action button configuration bundle. This bundle should be constructed
* with the same structure in {@link CustomTabsIntent.Builder}.
* @return Whether the operation was successful.
*/
protected abstract boolean updateVisuals(@NonNull CustomTabsSessionToken sessionToken,
@Nullable Bundle bundle);
/**
* Sends a request to create a two way postMessage channel between the client and the browser
* linked with the given {@link CustomTabsSession}.
*
* @param sessionToken The unique identifier for the session. Can not be null.
* @param postMessageOrigin A origin that the client is requesting to be identified as
* during the postMessage communication.
* @return Whether the implementation accepted the request. Note that returning true
* here doesn't mean an origin has already been assigned as the validation is
* asynchronous.
*/
protected abstract boolean requestPostMessageChannel(
@NonNull CustomTabsSessionToken sessionToken, @NonNull Uri postMessageOrigin);
/**
* Same as above method with specifying the target origin to establish communication with.
*
* @param sessionToken The unique identifier for the session. Can not be null.
* @param postMessageOrigin A origin that the client is requesting to be identified as
* during the postMessage communication.
* @param postMessageTargetOrigin The target Origin to establish PostMessageChannel with and
* send messages to.
* @param extras Reserved for future use.
* @return Whether the implementation accepted the request. Note that returning true
* here doesn't mean an origin has already been assigned as the validation is
* asynchronous.
*/
protected boolean requestPostMessageChannel(
@NonNull CustomTabsSessionToken sessionToken, @NonNull Uri postMessageOrigin,
@Nullable Uri postMessageTargetOrigin, @NonNull Bundle extras) {
return requestPostMessageChannel(sessionToken, postMessageOrigin);
}
/**
* Sends a postMessage request using the origin communicated via
* {@link CustomTabsService#requestPostMessageChannel(
*CustomTabsSessionToken, Uri)}. Fails when called before
* {@link PostMessageServiceConnection#notifyMessageChannelReady(Bundle)} is received on the
* client side.
*
* @param sessionToken The unique identifier for the session. Can not be null.
* @param message The message that is being sent.
* @param extras Reserved for future use.
* @return An integer constant about the postMessage request result. Will return
* {@link CustomTabsService#RESULT_SUCCESS} if successful.
*/
@Result
protected abstract int postMessage(@NonNull CustomTabsSessionToken sessionToken,
@NonNull String message, @Nullable Bundle extras);
/**
* Request to validate a relationship between the application and an origin.
*
* If this method returns true, the validation result will be provided through
* {@link CustomTabsCallback#onRelationshipValidationResult(int, Uri, boolean, Bundle)}.
* Otherwise the request didn't succeed. The client must call
* {@link CustomTabsClient#warmup(long)} before this.
*
* @param sessionToken The unique identifier for the session. Can not be null.
* @param relation Relation to check, must be one of the {@code CustomTabsService#RELATION_* }
* constants.
* @param origin Origin for the relation query.
* @param extras Reserved for future use.
* @return true if the request has been submitted successfully.
*/
protected abstract boolean validateRelationship(@NonNull CustomTabsSessionToken sessionToken,
@Relation int relation, @NonNull Uri origin, @Nullable Bundle extras);
/**
* Receive a file from client by given Uri, e.g. in order to display a large bitmap in a Custom
* Tab.
*
* Prior to calling this method, the client grants a read permission to the target
* Custom Tabs provider via {@link android.content.Context#grantUriPermission}.
*
* The file is read and processed (where applicable) synchronously.
*
* @param sessionToken The unique identifier for the session.
* @param uri {@link Uri} of the file.
* @param purpose Purpose of transferring this file, one of the constants enumerated in
* {@code CustomTabsService#FilePurpose}.
* @param extras Reserved for future use.
* @return {@code true} if the file was received successfully.
*/
protected abstract boolean receiveFile(@NonNull CustomTabsSessionToken sessionToken,
@NonNull Uri uri, @FilePurpose int purpose, @Nullable Bundle extras);
/**
* Returns whether the Engagement Signals API is available. The availability of the Engagement
* Signals API may change at runtime. If an {@link EngagementSignalsCallback} has been set, an
* {@link EngagementSignalsCallback#onSessionEnded} signal will be sent if the API becomes
* unavailable later.
*
* @param sessionToken The unique identifier for the session.
* @param extras Reserved for future use.
* @return Whether the Engagement Signals API is available. A false value means
* {@link #setEngagementSignalsCallback} will return false and not set the callback.
*/
protected boolean isEngagementSignalsApiAvailable(@NonNull CustomTabsSessionToken sessionToken,
@NonNull Bundle extras) {
return false;
}
/**
* Sets an {@link EngagementSignalsCallback} to execute callbacks for events related to
* the user's engagement with the webpage within the tab.
*
* @param sessionToken The unique identifier for the session.
* @param callback The {@link EngagementSignalsCallback} to execute the callbacks.
* @param extras Reserved for future use.
* @return Whether the callback connection is allowed. If false, no callbacks will be called for
* this session.
*/
// This is called by the implementation which already has the ability to decide on which
// thread to run the callbacks.
@SuppressWarnings("ExecutorRegistration")
protected boolean setEngagementSignalsCallback(
@NonNull CustomTabsSessionToken sessionToken,
@NonNull EngagementSignalsCallback callback, @NonNull Bundle extras) {
return false;
}
}