Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb875cb0 authored by Dianne Hackborn's avatar Dianne Hackborn Committed by Android (Google) Code Review
Browse files

Merge "Rework some of the voice interaction APIs."

parents 80c1210e 18f0d357
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -201,6 +201,7 @@ LOCAL_SRC_FILES += \
	core/java/android/service/trust/ITrustAgentServiceCallback.aidl \
	core/java/android/service/voice/IVoiceInteractionService.aidl \
	core/java/android/service/voice/IVoiceInteractionSession.aidl \
	core/java/android/service/voice/IVoiceInteractionSessionService.aidl \
	core/java/android/service/wallpaper/IWallpaperConnection.aidl \
	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
	core/java/android/service/wallpaper/IWallpaperService.aidl \
+30 −13
Original line number Diff line number Diff line
@@ -1009,6 +1009,7 @@ package android {
    field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
    field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
    field public static final int sequence = 16843815; // 0x1010427
    field public static final int sessionService = 16843850; // 0x101044a
    field public static final int settingsActivity = 16843301; // 0x1010225
    field public static final int shadowColor = 16843105; // 0x1010161
    field public static final int shadowDx = 16843106; // 0x1010162
@@ -1663,10 +1664,14 @@ package android {
    field public static final int decelerate_cubic = 17563651; // 0x10c0003
    field public static final int decelerate_quad = 17563649; // 0x10c0001
    field public static final int decelerate_quint = 17563653; // 0x10c0005
    field public static final int fast_out_linear_in = 17563663; // 0x10c000f
    field public static final int fast_out_slow_in = 17563661; // 0x10c000d
    field public static final int fast_out_linear_in = 17563667; // 0x10c0013
    field public static final int fast_out_slow_in = 17563665; // 0x10c0011
    field public static final int l_resource_pad1 = 17563664; // 0x10c0010
    field public static final int l_resource_pad2 = 17563663; // 0x10c000f
    field public static final int l_resource_pad3 = 17563662; // 0x10c000e
    field public static final int l_resource_pad4 = 17563661; // 0x10c000d
    field public static final int linear = 17563659; // 0x10c000b
    field public static final int linear_out_slow_in = 17563662; // 0x10c000e
    field public static final int linear_out_slow_in = 17563666; // 0x10c0012
    field public static final int overshoot = 17563656; // 0x10c0008
  }
@@ -4842,20 +4847,26 @@ package android.app {
  }
  public class VoiceInteractor {
    method public android.app.VoiceInteractor.Request startCommand(android.app.VoiceInteractor.Callback, java.lang.String, android.os.Bundle);
    method public android.app.VoiceInteractor.Request startConfirmation(android.app.VoiceInteractor.Callback, java.lang.String, android.os.Bundle);
    method public boolean submitRequest(android.app.VoiceInteractor.Request);
    method public boolean[] supportsCommands(java.lang.String[]);
  }
  public static class VoiceInteractor.Callback {
    ctor public VoiceInteractor.Callback();
    method public void onCancel(android.app.VoiceInteractor.Request);
    method public void onCommandResult(android.app.VoiceInteractor.Request, android.os.Bundle);
    method public void onConfirmationResult(android.app.VoiceInteractor.Request, boolean, android.os.Bundle);
  public static class VoiceInteractor.CommandRequest extends android.app.VoiceInteractor.Request {
    ctor public VoiceInteractor.CommandRequest(java.lang.String, android.os.Bundle);
    method public void onCommandResult(android.os.Bundle);
  }
  public static class VoiceInteractor.Request {
  public static class VoiceInteractor.ConfirmationRequest extends android.app.VoiceInteractor.Request {
    ctor public VoiceInteractor.ConfirmationRequest(java.lang.CharSequence, android.os.Bundle);
    method public void onConfirmationResult(boolean, android.os.Bundle);
  }
  public static abstract class VoiceInteractor.Request {
    ctor public VoiceInteractor.Request();
    method public void cancel();
    method public android.app.Activity getActivity();
    method public android.content.Context getContext();
    method public void onCancel();
  }
  public final class WallpaperInfo implements android.os.Parcelable {
@@ -24919,7 +24930,7 @@ package android.service.voice {
  public class VoiceInteractionService extends android.app.Service {
    ctor public VoiceInteractionService();
    method public android.os.IBinder onBind(android.content.Intent);
    method public void startVoiceActivity(android.content.Intent, android.service.voice.VoiceInteractionSession);
    method public void startVoiceActivity(android.content.Intent, android.os.Bundle);
    field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
    field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
  }
@@ -24938,10 +24949,16 @@ package android.service.voice {
  public static class VoiceInteractionSession.Request {
    method public void sendCancelResult();
    method public void sendCommandResult(android.os.Bundle);
    method public void sendCommandResult(boolean, android.os.Bundle);
    method public void sendConfirmResult(boolean, android.os.Bundle);
  }
  public abstract class VoiceInteractionSessionService extends android.app.Service {
    ctor public VoiceInteractionSessionService();
    method public android.os.IBinder onBind(android.content.Intent);
    method public abstract android.service.voice.VoiceInteractionSession onNewSession(android.os.Bundle);
  }
}
package android.service.wallpaper {
+1 −1
Original line number Diff line number Diff line
@@ -5454,7 +5454,7 @@ public class Activity extends ContextThemeWrapper
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        mVoiceInteractor = voiceInteractor != null
                ? new VoiceInteractor(this, voiceInteractor, Looper.myLooper()) : null;
                ? new VoiceInteractor(this, this, voiceInteractor, Looper.myLooper()) : null;

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
+151 −111
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractorCallback;
@@ -39,179 +40,218 @@ public class VoiceInteractor {
    static final boolean DEBUG = true;

    final Context mContext;
    final Activity mActivity;
    final IVoiceInteractor mInteractor;
    final HandlerCaller mHandlerCaller;
    final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
        @Override
        public void executeMessage(Message msg) {
            SomeArgs args = (SomeArgs)msg.obj;
            Request request;
            switch (msg.what) {
                case MSG_CONFIRMATION_RESULT:
                    request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
                    if (DEBUG) Log.d(TAG, "onConfirmResult: req="
                            + ((IVoiceInteractorRequest)args.arg2).asBinder()
                            + " confirmed=" + msg.arg1 + " result=" + args.arg3);
                    ((Callback)args.arg1).onConfirmationResult(
                            findRequest((IVoiceInteractorRequest)args.arg2),
                            msg.arg1 != 0, (Bundle)args.arg3);
                            + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
                            + " confirmed=" + msg.arg1 + " result=" + args.arg2);
                    if (request != null) {
                        ((ConfirmationRequest)request).onConfirmationResult(msg.arg1 != 0,
                                (Bundle) args.arg2);
                        request.clear();
                    }
                    break;
                case MSG_COMMAND_RESULT:
                    request = pullRequest((IVoiceInteractorRequest)args.arg1, msg.arg1 != 0);
                    if (DEBUG) Log.d(TAG, "onCommandResult: req="
                            + ((IVoiceInteractorRequest)args.arg2).asBinder()
                            + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
                            + " result=" + args.arg2);
                    ((Callback)args.arg1).onCommandResult(
                            findRequest((IVoiceInteractorRequest) args.arg2),
                            (Bundle) args.arg3);
                    if (request != null) {
                        ((CommandRequest)request).onCommandResult((Bundle) args.arg2);
                        if (msg.arg1 != 0) {
                            request.clear();
                        }
                    }
                    break;
                case MSG_CANCEL_RESULT:
                    request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
                    if (DEBUG) Log.d(TAG, "onCancelResult: req="
                            + ((IVoiceInteractorRequest)args.arg2).asBinder());
                    ((Callback)args.arg1).onCancel(
                            findRequest((IVoiceInteractorRequest) args.arg2));
                            + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request);
                    if (request != null) {
                        request.onCancel();
                        request.clear();
                    }
                    break;
            }
        }
    };

    final WeakHashMap<IBinder, Request> mActiveRequests = new WeakHashMap<IBinder, Request>();

    static final int MSG_CONFIRMATION_RESULT = 1;
    static final int MSG_COMMAND_RESULT = 2;
    static final int MSG_CANCEL_RESULT = 3;

    public static class Request {
        final IVoiceInteractorRequest mRequestInterface;

        Request(IVoiceInteractorRequest requestInterface) {
            mRequestInterface = requestInterface;
        }

        public void cancel() {
            try {
                mRequestInterface.cancel();
            } catch (RemoteException e) {
                Log.w(TAG, "Voice interactor has died", e);
            }
        }
    }

    public static class Callback {
        VoiceInteractor mInteractor;

        final IVoiceInteractorCallback.Stub mWrapper = new IVoiceInteractorCallback.Stub() {
    final IVoiceInteractorCallback.Stub mCallback = new IVoiceInteractorCallback.Stub() {
        @Override
        public void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
                Bundle result) {
                mInteractor.mHandlerCaller.sendMessage(mInteractor.mHandlerCaller.obtainMessageIOOO(
                        MSG_CONFIRMATION_RESULT, confirmed ? 1 : 0, Callback.this, request,
                        result));
            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(
                    MSG_CONFIRMATION_RESULT, confirmed ? 1 : 0, request, result));
        }

        @Override
            public void deliverCommandResult(IVoiceInteractorRequest request, Bundle result) {
                mInteractor.mHandlerCaller.sendMessage(mInteractor.mHandlerCaller.obtainMessageOOO(
                        MSG_COMMAND_RESULT, Callback.this, request, result));
        public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete,
                Bundle result) {
            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(
                    MSG_COMMAND_RESULT, complete ? 1 : 0, request, result));
        }

        @Override
        public void deliverCancel(IVoiceInteractorRequest request) throws RemoteException {
                mInteractor.mHandlerCaller.sendMessage(mInteractor.mHandlerCaller.obtainMessageOO(
                        MSG_CANCEL_RESULT, Callback.this, request));
            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(
                    MSG_CANCEL_RESULT, request));
        }
    };

        public void onConfirmationResult(Request request, boolean confirmed, Bundle result) {
        }
    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();

    static final int MSG_CONFIRMATION_RESULT = 1;
    static final int MSG_COMMAND_RESULT = 2;
    static final int MSG_CANCEL_RESULT = 3;

        public void onCommandResult(Request request, Bundle result) {
    public static abstract class Request {
        IVoiceInteractorRequest mRequestInterface;
        Context mContext;
        Activity mActivity;

        public Request() {
        }

        public void onCancel(Request request) {
        public void cancel() {
            try {
                mRequestInterface.cancel();
            } catch (RemoteException e) {
                Log.w(TAG, "Voice interactor has died", e);
            }
        }

    VoiceInteractor(Context context, IVoiceInteractor interactor, Looper looper) {
        mContext = context;
        mInteractor = interactor;
        mHandlerCaller = new HandlerCaller(context, looper, mHandlerCallerCallback, true);
        public Context getContext() {
            return mContext;
        }

    Request storeRequest(IVoiceInteractorRequest request) {
        synchronized (mActiveRequests) {
            Request req = new Request(request);
            mActiveRequests.put(request.asBinder(), req);
            return req;
        }
        public Activity getActivity() {
            return mActivity;
        }

    Request findRequest(IVoiceInteractorRequest request) {
        synchronized (mActiveRequests) {
            Request req = mActiveRequests.get(request.asBinder());
            if (req == null) {
                throw new IllegalStateException("Received callback without active request: "
                        + request);
        public void onCancel() {
        }
            return req;

        void clear() {
            mRequestInterface = null;
            mContext = null;
            mActivity = null;
        }

        abstract IVoiceInteractorRequest submit(IVoiceInteractor interactor,
                String packageName, IVoiceInteractorCallback callback) throws RemoteException;
    }

    public static class ConfirmationRequest extends Request {
        final CharSequence mPrompt;
        final Bundle mExtras;

        /**
     * Asynchronously confirms an operation with the user via the trusted system
     * VoiceinteractionService.  This allows an Activity to complete an unsafe operation that
         * Confirms an operation with the user via the trusted system
         * VoiceInteractionService.  This allows an Activity to complete an unsafe operation that
         * would require the user to touch the screen when voice interaction mode is not enabled.
     * The result of the confirmation will be returned by calling the
     * {@link Callback#onConfirmationResult Callback.onConfirmationResult} method.
         * The result of the confirmation will be returned through an asynchronous call to
         * either {@link #onConfirmationResult(boolean, android.os.Bundle)} or
         * {@link #onCancel()}.
         *
     * In some cases this may be a simple yes / no confirmation or the confirmation could
         * <p>In some cases this may be a simple yes / no confirmation or the confirmation could
         * include context information about how the action will be completed
     * (e.g. booking a cab might include details about how long until the cab arrives) so the user
     * can give informed consent.
     * @param callback Required callback target for interaction results.
     * @param prompt Optional confirmation text to read to the user as the action being confirmed.
         * (e.g. booking a cab might include details about how long until the cab arrives)
         * so the user can give a confirmation.
         * @param prompt Optional confirmation text to read to the user as the action being
         * confirmed.
         * @param extras Additional optional information.
     * @return Returns a new {@link Request} object representing this operation.
         */
    public Request startConfirmation(Callback callback, String prompt, Bundle extras) {
        try {
            callback.mInteractor = this;
            Request req = storeRequest(mInteractor.startConfirmation(
                    mContext.getOpPackageName(), callback.mWrapper, prompt, extras));
            if (DEBUG) Log.d(TAG, "startConfirmation: req=" + req.mRequestInterface.asBinder()
                    + " prompt=" + prompt + " extras=" + extras);
            return req;
        } catch (RemoteException e) {
            throw new RuntimeException("Voice interactor has died", e);
        public ConfirmationRequest(CharSequence prompt, Bundle extras) {
            mPrompt = prompt;
            mExtras = extras;
        }

        public void onConfirmationResult(boolean confirmed, Bundle result) {
        }

        IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
                IVoiceInteractorCallback callback) throws RemoteException {
            return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras);
        }
   }

    public static class CommandRequest extends Request {
        final String mCommand;
        final Bundle mArgs;

        /**
     * Asynchronously executes a command using the trusted system VoiceinteractionService.
         * Execute a command using the trusted system VoiceInteractionService.
         * This allows an Activity to request additional information from the user needed to
         * complete an action (e.g. booking a table might have several possible times that the
         * user could select from or an app might need the user to agree to a terms of service).
         * The result of the confirmation will be returned through an asynchronous call to
         * either {@link #onCommandResult(android.os.Bundle)} or
         * {@link #onCancel()}.
         *
     * The command is a string that describes the generic operation to be performed.
         * <p>The command is a string that describes the generic operation to be performed.
         * The command will determine how the properties in extras are interpreted and the set of
         * available commands is expected to grow over time.  An example might be
         * "com.google.voice.commands.REQUEST_NUMBER_BAGS" to request the number of bags as part of
         * airline check-in.  (This is not an actual working example.)
     * The result of the command will be returned by calling the
     * {@link Callback#onCommandResult Callback.onCommandResult} method.
         *
     * @param callback Required callback target for interaction results.
     * @param command
     * @param extras
     * @return Returns a new {@link Request} object representing this operation.
         * @param command The desired command to perform.
         * @param args Additional arguments to control execution of the command.
         */
    public Request startCommand(Callback callback, String command, Bundle extras) {
        try {
            callback.mInteractor = this;
            Request req = storeRequest(mInteractor.startCommand(
                    mContext.getOpPackageName(), callback.mWrapper, command, extras));
            if (DEBUG) Log.d(TAG, "startCommand: req=" + req.mRequestInterface.asBinder()
                    + " command=" + command + " extras=" + extras);
        public CommandRequest(String command, Bundle args) {
            mCommand = command;
            mArgs = args;
        }

        public void onCommandResult(Bundle result) {
        }

        IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
                IVoiceInteractorCallback callback) throws RemoteException {
            return interactor.startConfirmation(packageName, callback, mCommand, mArgs);
        }
   }

    VoiceInteractor(Context context, Activity activity, IVoiceInteractor interactor,
            Looper looper) {
        mContext = context;
        mActivity = activity;
        mInteractor = interactor;
        mHandlerCaller = new HandlerCaller(context, looper, mHandlerCallerCallback, true);
    }

    Request pullRequest(IVoiceInteractorRequest request, boolean complete) {
        synchronized (mActiveRequests) {
            Request req = mActiveRequests.get(request.asBinder());
            if (req != null && complete) {
                mActiveRequests.remove(request.asBinder());
            }
            return req;
        }
    }

    public boolean submitRequest(Request request) {
        try {
            IVoiceInteractorRequest ireq = request.submit(mInteractor,
                    mContext.getOpPackageName(), mCallback);
            request.mRequestInterface = ireq;
            request.mContext = mContext;
            request.mActivity = mActivity;
            synchronized (mActiveRequests) {
                mActiveRequests.put(ireq.asBinder(), request);
            }
            return true;
        } catch (RemoteException e) {
            throw new RuntimeException("Voice interactor has died", e);
            Log.w(TAG, "Remove voice interactor service died", e);
            return false;
        }
    }

+28 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.voice;

import android.os.Bundle;

import android.service.voice.IVoiceInteractionSession;

/**
 * @hide
 */
oneway interface IVoiceInteractionSessionService {
    void newSession(IBinder token, in Bundle args);
}
Loading