/*
 * Copyright (C) 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 android.service.textclassifier;

import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Slog;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;

import com.android.internal.util.Preconditions;

Abstract base class for the TextClassifier service.

A TextClassifier service provides text classification related features for the system. The system's default TextClassifierService is configured in config_defaultTextClassifierService. If this config has no value, a TextClassifierImpl is loaded in the calling app's process.

See: TextClassifier. See: TextClassificationManager.

Include the following in the manifest:

<service android:name=".YourTextClassifierService"
         android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE">
    <intent-filter>
        <action android:name="android.service.textclassifier.TextClassifierService" />
    </intent-filter>
</service>
See Also:
@hide
/** * Abstract base class for the TextClassifier service. * * <p>A TextClassifier service provides text classification related features for the system. * The system's default TextClassifierService is configured in * {@code config_defaultTextClassifierService}. If this config has no value, a * {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process. * * <p>See: {@link TextClassifier}. * See: {@link TextClassificationManager}. * * <p>Include the following in the manifest: * * <pre> * {@literal * <service android:name=".YourTextClassifierService" * android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE"> * <intent-filter> * <action android:name="android.service.textclassifier.TextClassifierService" /> * </intent-filter> * </service>}</pre> * * @see TextClassifier * @hide */
@SystemApi public abstract class TextClassifierService extends Service { private static final String LOG_TAG = "TextClassifierService";
The Intent that must be declared as handled by the service. To be supported, the service must also require the BIND_TEXTCLASSIFIER_SERVICE.BIND_TEXTCLASSIFIER_SERVICE permission so that other applications can not abuse it.
/** * The {@link Intent} that must be declared as handled by the service. * To be supported, the service must also require the * {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so * that other applications can not abuse it. */
@SystemApi public static final String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService"; private final ITextClassifierService.Stub mBinder = new ITextClassifierService.Stub() { // TODO(b/72533911): Implement cancellation signal @NonNull private final CancellationSignal mCancellationSignal = new CancellationSignal();
{@inheritDoc}
/** {@inheritDoc} */
@Override public void onSuggestSelection( TextClassificationSessionId sessionId, TextSelection.Request request, ITextSelectionCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); TextClassifierService.this.onSuggestSelection( request.getText(), request.getStartIndex(), request.getEndIndex(), TextSelection.Options.from(sessionId, request), mCancellationSignal, new Callback<TextSelection>() { @Override public void onSuccess(TextSelection result) { try { callback.onSuccess(result); } catch (RemoteException e) { Slog.d(LOG_TAG, "Error calling callback"); } } @Override public void onFailure(CharSequence error) { try { if (callback.asBinder().isBinderAlive()) { callback.onFailure(); } } catch (RemoteException e) { Slog.d(LOG_TAG, "Error calling callback"); } } }); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void onClassifyText( TextClassificationSessionId sessionId, TextClassification.Request request, ITextClassificationCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); TextClassifierService.this.onClassifyText( request.getText(), request.getStartIndex(), request.getEndIndex(), TextClassification.Options.from(sessionId, request), mCancellationSignal, new Callback<TextClassification>() { @Override public void onSuccess(TextClassification result) { try { callback.onSuccess(result); } catch (RemoteException e) { Slog.d(LOG_TAG, "Error calling callback"); } } @Override public void onFailure(CharSequence error) { try { callback.onFailure(); } catch (RemoteException e) { Slog.d(LOG_TAG, "Error calling callback"); } } }); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void onGenerateLinks( TextClassificationSessionId sessionId, TextLinks.Request request, ITextLinksCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); TextClassifierService.this.onGenerateLinks( request.getText(), TextLinks.Options.from(sessionId, request), mCancellationSignal, new Callback<TextLinks>() { @Override public void onSuccess(TextLinks result) { try { callback.onSuccess(result); } catch (RemoteException e) { Slog.d(LOG_TAG, "Error calling callback"); } } @Override public void onFailure(CharSequence error) { try { callback.onFailure(); } catch (RemoteException e) { Slog.d(LOG_TAG, "Error calling callback"); } } }); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void onSelectionEvent( TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException { Preconditions.checkNotNull(event); TextClassifierService.this.onSelectionEvent(sessionId, event); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void onCreateTextClassificationSession( TextClassificationContext context, TextClassificationSessionId sessionId) throws RemoteException { Preconditions.checkNotNull(context); Preconditions.checkNotNull(sessionId); TextClassifierService.this.onCreateTextClassificationSession(context, sessionId); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId) throws RemoteException { TextClassifierService.this.onDestroyTextClassificationSession(sessionId); } }; @Nullable @Override public final IBinder onBind(Intent intent) { if (SERVICE_INTERFACE.equals(intent.getAction())) { return mBinder; } return null; }
Returns suggested text selection start and end indices, recognized entity types, and their associated confidence scores. The entity types are ordered from highest to lowest scoring.
Params:
  • sessionId – the session id
  • request – the text selection request
  • cancellationSignal – object to watch for canceling the current operation
  • callback – the callback to return the result to
/** * Returns suggested text selection start and end indices, recognized entity types, and their * associated confidence scores. The entity types are ordered from highest to lowest scoring. * * @param sessionId the session id * @param request the text selection request * @param cancellationSignal object to watch for canceling the current operation * @param callback the callback to return the result to */
public abstract void onSuggestSelection( @Nullable TextClassificationSessionId sessionId, @NonNull TextSelection.Request request, @NonNull CancellationSignal cancellationSignal, @NonNull Callback<TextSelection> callback); // TODO: Remove once apps can build against the latest sdk.
@hide
/** @hide */
public void onSuggestSelection( @NonNull CharSequence text, @IntRange(from = 0) int selectionStartIndex, @IntRange(from = 0) int selectionEndIndex, @Nullable TextSelection.Options options, @NonNull CancellationSignal cancellationSignal, @NonNull Callback<TextSelection> callback) { final TextClassificationSessionId sessionId = options.getSessionId(); final TextSelection.Request request = options.getRequest() != null ? options.getRequest() : new TextSelection.Request.Builder( text, selectionStartIndex, selectionEndIndex) .setDefaultLocales(options.getDefaultLocales()) .build(); onSuggestSelection(sessionId, request, cancellationSignal, callback); }
Classifies the specified text and returns a TextClassification object that can be used to generate a widget for handling the classified text.
Params:
  • sessionId – the session id
  • request – the text classification request
  • cancellationSignal – object to watch for canceling the current operation
  • callback – the callback to return the result to
/** * Classifies the specified text and returns a {@link TextClassification} object that can be * used to generate a widget for handling the classified text. * * @param sessionId the session id * @param request the text classification request * @param cancellationSignal object to watch for canceling the current operation * @param callback the callback to return the result to */
public abstract void onClassifyText( @Nullable TextClassificationSessionId sessionId, @NonNull TextClassification.Request request, @NonNull CancellationSignal cancellationSignal, @NonNull Callback<TextClassification> callback); // TODO: Remove once apps can build against the latest sdk.
@hide
/** @hide */
public void onClassifyText( @NonNull CharSequence text, @IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex, @Nullable TextClassification.Options options, @NonNull CancellationSignal cancellationSignal, @NonNull Callback<TextClassification> callback) { final TextClassificationSessionId sessionId = options.getSessionId(); final TextClassification.Request request = options.getRequest() != null ? options.getRequest() : new TextClassification.Request.Builder( text, startIndex, endIndex) .setDefaultLocales(options.getDefaultLocales()) .setReferenceTime(options.getReferenceTime()) .build(); onClassifyText(sessionId, request, cancellationSignal, callback); }
Generates and returns a TextLinks that may be applied to the text to annotate it with links information.
Params:
  • sessionId – the session id
  • request – the text classification request
  • cancellationSignal – object to watch for canceling the current operation
  • callback – the callback to return the result to
/** * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with * links information. * * @param sessionId the session id * @param request the text classification request * @param cancellationSignal object to watch for canceling the current operation * @param callback the callback to return the result to */
public abstract void onGenerateLinks( @Nullable TextClassificationSessionId sessionId, @NonNull TextLinks.Request request, @NonNull CancellationSignal cancellationSignal, @NonNull Callback<TextLinks> callback); // TODO: Remove once apps can build against the latest sdk.
@hide
/** @hide */
public void onGenerateLinks( @NonNull CharSequence text, @Nullable TextLinks.Options options, @NonNull CancellationSignal cancellationSignal, @NonNull Callback<TextLinks> callback) { final TextClassificationSessionId sessionId = options.getSessionId(); final TextLinks.Request request = options.getRequest() != null ? options.getRequest() : new TextLinks.Request.Builder(text) .setDefaultLocales(options.getDefaultLocales()) .setEntityConfig(options.getEntityConfig()) .build(); onGenerateLinks(sessionId, request, cancellationSignal, callback); }
Writes the selection event. This is called when a selection event occurs. e.g. user changed selection; or smart selection happened.

The default implementation ignores the event.

Params:
  • sessionId – the session id
  • event – the selection event
/** * Writes the selection event. * This is called when a selection event occurs. e.g. user changed selection; or smart selection * happened. * * <p>The default implementation ignores the event. * * @param sessionId the session id * @param event the selection event */
public void onSelectionEvent( @Nullable TextClassificationSessionId sessionId, @NonNull SelectionEvent event) {}
Creates a new text classification session for the specified context.
Params:
  • context – the text classification context
  • sessionId – the session's Id
/** * Creates a new text classification session for the specified context. * * @param context the text classification context * @param sessionId the session's Id */
public void onCreateTextClassificationSession( @NonNull TextClassificationContext context, @NonNull TextClassificationSessionId sessionId) {}
Destroys the text classification session identified by the specified sessionId.
Params:
  • sessionId – the id of the session to destroy
/** * Destroys the text classification session identified by the specified sessionId. * * @param sessionId the id of the session to destroy */
public void onDestroyTextClassificationSession( @NonNull TextClassificationSessionId sessionId) {}
Returns a TextClassifier that runs in this service's process. If the local TextClassifier is disabled, this returns TextClassifier.NO_OP.
/** * Returns a TextClassifier that runs in this service's process. * If the local TextClassifier is disabled, this returns {@link TextClassifier#NO_OP}. */
public final TextClassifier getLocalTextClassifier() { final TextClassificationManager tcm = getSystemService(TextClassificationManager.class); if (tcm != null) { return tcm.getTextClassifier(TextClassifier.LOCAL); } return TextClassifier.NO_OP; }
Callbacks for TextClassifierService results.
Type parameters:
  • <T> – the type of the result
@hide
/** * Callbacks for TextClassifierService results. * * @param <T> the type of the result * @hide */
@SystemApi public interface Callback<T> {
Returns the result.
/** * Returns the result. */
void onSuccess(T result);
Signals a failure.
/** * Signals a failure. */
void onFailure(CharSequence error); }
Returns the component name of the system default textclassifier service if it can be found on the system. Otherwise, returns null.
@hide
/** * Returns the component name of the system default textclassifier service if it can be found * on the system. Otherwise, returns null. * @hide */
@Nullable public static ComponentName getServiceComponentName(Context context) { final String packageName = context.getPackageManager().getSystemTextClassifierPackageName(); if (TextUtils.isEmpty(packageName)) { Slog.d(LOG_TAG, "No configured system TextClassifierService"); return null; } final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName); final ResolveInfo ri = context.getPackageManager().resolveService(intent, PackageManager.MATCH_SYSTEM_ONLY); if ((ri == null) || (ri.serviceInfo == null)) { Slog.w(LOG_TAG, String.format("Package or service not found in package %s", packageName)); return null; } final ServiceInfo si = ri.serviceInfo; final String permission = si.permission; if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) { return si.getComponentName(); } Slog.w(LOG_TAG, String.format( "Service %s should require %s permission. Found %s permission", si.getComponentName(), Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE, si.permission)); return null; } }