blob: 1bb81b1487afdf46313e914088ef09b25eaba17c [file] [log] [blame]
/*
* 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.app.contentsuggestions;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.util.SyncResultReceiver;
import java.util.List;
import java.util.concurrent.Executor;
/**
* When provided with content from an app, can suggest selections and classifications of that
* content.
*
* <p>The content is mainly a snapshot of a running task, the selections will be text and image
* selections with that image content. These mSelections can then be classified to find actions and
* entities on those selections.
*
* <p>Only accessible to blessed components such as Overview.
*
* @hide
*/
@SystemApi
public final class ContentSuggestionsManager {
private static final String TAG = ContentSuggestionsManager.class.getSimpleName();
/**
* Timeout for calls to system_server.
*/
private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
@Nullable
private final IContentSuggestionsManager mService;
@NonNull
private final int mUser;
/** @hide */
public ContentSuggestionsManager(
@UserIdInt int userId, @Nullable IContentSuggestionsManager service) {
mService = service;
mUser = userId;
}
/**
* Hints to the system that a new context image for the provided task should be sent to the
* system content suggestions service.
*
* @param taskId of the task to snapshot.
* @param imageContextRequestExtras sent with with request to provide implementation specific
* extra information.
*/
public void provideContextImage(
int taskId, @NonNull Bundle imageContextRequestExtras) {
if (mService == null) {
Log.e(TAG, "provideContextImage called, but no ContentSuggestionsManager configured");
return;
}
try {
mService.provideContextImage(mUser, taskId, imageContextRequestExtras);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Suggest content selections, based on the provided task id and optional
* location on screen provided in the request. Called after provideContextImage().
* The result can be passed to
* {@link #classifyContentSelections(ClassificationsRequest, Executor, ClassificationsCallback)}
* to classify actions and entities on these selections.
*
* @param request containing the task and point location.
* @param callbackExecutor to execute the provided callback on.
* @param callback to receive the selections.
*/
public void suggestContentSelections(
@NonNull SelectionsRequest request,
@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull SelectionsCallback callback) {
if (mService == null) {
Log.e(TAG,
"suggestContentSelections called, but no ContentSuggestionsManager configured");
return;
}
try {
mService.suggestContentSelections(
mUser, request, new SelectionsCallbackWrapper(callback, callbackExecutor));
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Classify actions and entities in content selections, as returned from
* suggestContentSelections. Note these selections may be modified by the
* caller before being passed here.
*
* @param request containing the selections to classify.
* @param callbackExecutor to execute the provided callback on.
* @param callback to receive the classifications.
*/
public void classifyContentSelections(
@NonNull ClassificationsRequest request,
@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull ClassificationsCallback callback) {
if (mService == null) {
Log.e(TAG, "classifyContentSelections called, "
+ "but no ContentSuggestionsManager configured");
return;
}
try {
mService.classifyContentSelections(
mUser, request, new ClassificationsCallbackWrapper(callback, callbackExecutor));
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Report telemetry for interaction with suggestions / classifications.
*
* @param requestId the id for the associated interaction
* @param interaction to report back to the system content suggestions service.
*/
public void notifyInteraction(
@NonNull String requestId, @NonNull Bundle interaction) {
if (mService == null) {
Log.e(TAG, "notifyInteraction called, but no ContentSuggestionsManager configured");
return;
}
try {
mService.notifyInteraction(mUser, requestId, interaction);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Indicates that Content Suggestions is available and enabled for the provided user. That is,
* has an implementation and not disabled through device management.
*
* @return {@code true} if Content Suggestions is enabled and available for the provided user.
*/
public boolean isEnabled() {
if (mService == null) {
return false;
}
SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.isEnabled(mUser, receiver);
return receiver.getIntResult() != 0;
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
return false;
}
/**
* Callback to receive content selections from
* {@link #suggestContentSelections(SelectionsRequest, Executor, SelectionsCallback)}.
*/
public interface SelectionsCallback {
/**
* Async callback called when the content suggestions service has selections available.
* These can be modified and sent back to the manager for classification. The contents of
* the selection is implementation dependent.
*
* @param statusCode as defined by the implementation of content suggestions service.
* @param selections not {@code null}, but can be size {@code 0}.
*/
void onContentSelectionsAvailable(
int statusCode, @NonNull List<ContentSelection> selections);
}
/**
* Callback to receive classifications from
* {@link #classifyContentSelections(ClassificationsRequest, Executor, ClassificationsCallback)}
*/
public interface ClassificationsCallback {
/**
* Async callback called when the content suggestions service has classified selections. The
* contents of the classification is implementation dependent.
*
* @param statusCode as defined by the implementation of content suggestions service.
* @param classifications not {@code null}, but can be size {@code 0}.
*/
void onContentClassificationsAvailable(int statusCode,
@NonNull List<ContentClassification> classifications);
}
private static class SelectionsCallbackWrapper extends ISelectionsCallback.Stub {
private final SelectionsCallback mCallback;
private final Executor mExecutor;
SelectionsCallbackWrapper(
@NonNull SelectionsCallback callback, @NonNull Executor executor) {
mCallback = callback;
mExecutor = executor;
}
@Override
public void onContentSelectionsAvailable(
int statusCode, List<ContentSelection> selections) {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() ->
mCallback.onContentSelectionsAvailable(statusCode, selections));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
private static final class ClassificationsCallbackWrapper extends
IClassificationsCallback.Stub {
private final ClassificationsCallback mCallback;
private final Executor mExecutor;
ClassificationsCallbackWrapper(@NonNull ClassificationsCallback callback,
@NonNull Executor executor) {
mCallback = callback;
mExecutor = executor;
}
@Override
public void onContentClassificationsAvailable(
int statusCode, List<ContentClassification> classifications) {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(() ->
mCallback.onContentClassificationsAvailable(statusCode, classifications));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
}