OSDN Git Service

Tweak the HotwordRecognizer API
authorSandeep Siddhartha <sansid@google.com>
Tue, 27 Aug 2013 20:09:41 +0000 (13:09 -0700)
committerSandeep Siddhartha <sansid@google.com>
Wed, 28 Aug 2013 23:27:36 +0000 (16:27 -0700)
- Make it more intuitive by enforcing callers to set a listener while
  starting recognition
- Fix TODO to resolve the ComponentName if none is provided
- Unbind from the Hotword service if stopRecognition is called
- Get rid of the callback in onStopHotwordRecognition

Change-Id: I8edad0ef0f3671283aeb51eaed1d3b8dab01baa0

core/java/android/speech/hotword/HotwordRecognitionService.java
core/java/android/speech/hotword/HotwordRecognizer.java

index c16d2a9..521d06d 100644 (file)
@@ -21,7 +21,6 @@ import android.annotation.SdkConstant.SdkConstantType;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -113,8 +112,9 @@ public abstract class HotwordRecognitionService extends Service {
                 listener.onHotwordError(HotwordRecognizer.ERROR_RECOGNIZER_BUSY);
                 Log.w(TAG, "stopRecognition called by a different caller - ignoring");
             } else { // the correct state
-                HotwordRecognitionService.this.onStopHotwordRecognition(mCurrentCallback);
+                mCurrentCallback.onHotwordRecognitionStopped();
                 mCurrentCallback = null;
+                HotwordRecognitionService.this.onStopHotwordRecognition();
             }
         } catch (RemoteException e) { // occurs if onError fails
             if (DBG) Log.d(TAG, "onError call from stopRecognition failed");
@@ -139,27 +139,6 @@ public abstract class HotwordRecognitionService extends Service {
     }
 
     /**
-     * Checks whether the caller has sufficient permissions
-     *
-     * @param listener to send the error message to in case of error
-     * @return {@code true} if the caller has enough permissions, {@code false} otherwise
-     */
-    private boolean checkPermissions(IHotwordRecognitionListener listener) {
-        if (DBG) Log.d(TAG, "checkPermissions");
-        if (checkCallingOrSelfPermission(
-                android.Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        }
-        try {
-            Log.e(TAG, "Recognition service called without RECORD_AUDIO permissions");
-            listener.onHotwordError(HotwordRecognizer.ERROR_FAILED);
-        } catch (RemoteException e) {
-            Log.e(TAG, "onHotwordError(ERROR_INSUFFICIENT_PERMISSIONS) message failed", e);
-        }
-        return false;
-    }
-
-    /**
      * Notifies the service to start a recognition.
      *
      * @param callback that receives the callbacks from the service.
@@ -168,10 +147,8 @@ public abstract class HotwordRecognitionService extends Service {
 
     /**
      * Notifies the service to stop recognition.
-     *
-     * @param callback that receives the callbacks from the service.
      */
-    public abstract void onStopHotwordRecognition(Callback callback);
+    public abstract void onStopHotwordRecognition();
 
     /** Binder of the hotword recognition service */
     private static class RecognitionServiceBinder extends IHotwordRecognitionService.Stub {
@@ -183,7 +160,7 @@ public abstract class HotwordRecognitionService extends Service {
 
         public void startHotwordRecognition(IHotwordRecognitionListener listener) {
             if (DBG) Log.d(TAG, "startRecognition called by: " + listener.asBinder());
-            if (mInternalService != null && mInternalService.checkPermissions(listener)) {
+            if (mInternalService != null) {
                 mInternalService.mHandler.sendMessage(
                         Message.obtain(mInternalService.mHandler, MSG_START_RECOGNITION, listener));
             }
index c6bd1f3..82cec10 100644 (file)
@@ -21,6 +21,8 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.os.Handler;
@@ -82,7 +84,6 @@ public class HotwordRecognizer {
     /** action codes */
     private static final int MSG_START = 1;
     private static final int MSG_STOP = 2;
-    private final static int MSG_CHANGE_LISTENER = 3;
 
     /** The underlying HotwordRecognitionService endpoint */
     private IHotwordRecognitionService mService;
@@ -107,9 +108,6 @@ public class HotwordRecognizer {
                 case MSG_STOP:
                     handleStopRecognition();
                     break;
-                case MSG_CHANGE_LISTENER:
-                    handleChangeListener((HotwordRecognitionListener) msg.obj);
-                    break;
             }
         }
     };
@@ -138,24 +136,38 @@ public class HotwordRecognizer {
     }
 
     /**
-     * Factory method to create a new {@code HotwordRecognizer}. Please note that
-     * {@link #setRecognitionListener(HotwordRecognitionListener)}
-     * should be called before dispatching any command to the created {@code HotwordRecognizer},
-     * otherwise no notifications will be received.
+     * Factory method to create a new {@code HotwordRecognizer}.
      *
      * @param context in which to create {@code HotwordRecognizer}
      * @return a new {@code HotwordRecognizer}
      */
     public static HotwordRecognizer createHotwordRecognizer(final Context context) {
-        return createHotwordRecognizer(context, null);
+        ComponentName serviceComponent = null;
+        // Resolve to a default ComponentName.
+        final List<ResolveInfo> list = context.getPackageManager().queryIntentServices(
+                new Intent(HotwordRecognitionService.SERVICE_INTERFACE), 0);
+        for (int i = 0; i < list.size(); i++) {
+            final ResolveInfo ri = list.get(i);
+            if (!ri.serviceInfo.enabled) {
+                continue;
+            }
+            if ((ri.serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM)
+                    != PackageManager.MATCH_DEFAULT_ONLY) {
+                serviceComponent = new ComponentName(
+                        ri.serviceInfo.packageName, ri.serviceInfo.name);
+                break;
+            }
+        }
+        // If all else fails, pick the first one.
+        if (serviceComponent == null && !list.isEmpty()) {
+            serviceComponent =  new ComponentName(
+                    list.get(0).serviceInfo.packageName, list.get(0).serviceInfo.name);
+        }
+        return createHotwordRecognizer(context, serviceComponent);
     }
 
-
     /**
-     * Factory method to create a new {@code HotwordRecognizer}. Please note that
-     * {@link #setRecognitionListener(HotwordRecognitionListener)}
-     * should be called before dispatching any command to the created {@code HotwordRecognizer},
-     * otherwise no notifications will be received.
+     * Factory method to create a new {@code HotwordRecognizer}.
      *
      * Use this version of the method to specify a specific service to direct this
      * {@link HotwordRecognizer} to. Normally you would not use this; use
@@ -177,40 +189,26 @@ public class HotwordRecognizer {
     }
 
     /**
-     * Sets the listener that will receive all the callbacks. The previous unfinished commands will
-     * be executed with the old listener, while any following command will be executed with the new
-     * listener.
+     * Starts recognizing hotword and sets the listener that will receive the callbacks.
      *
      * @param listener listener that will receive all the callbacks from the created
      *        {@link HotwordRecognizer}, this must not be null.
      */
-    public void setRecognitionListener(HotwordRecognitionListener listener) {
-        checkIsCalledFromMainThread();
-        putMessage(Message.obtain(mHandler, MSG_CHANGE_LISTENER, listener));
-    }
-
-    /**
-     * Starts recognizing hotword. Please note that
-     * {@link #setRecognitionListener(HotwordRecognitionListener)} should be called beforehand,
-     * otherwise no notifications will be received.
-     */
-    public void startRecognition() {
+    public void startRecognition(HotwordRecognitionListener listener) {
         checkIsCalledFromMainThread();
         if (mConnection == null) { // first time connection
-            mConnection = new Connection();
+            if (listener == null) {
+                throw new IllegalArgumentException("listener must not be null");
+            }
 
+            mConnection = new Connection();
             Intent serviceIntent = new Intent(HotwordRecognitionService.SERVICE_INTERFACE);
-
+            mListener.mInternalListener = listener;
 
             if (mServiceComponent == null) {
-                // TODO: Resolve the ComponentName here and use it.
-                String serviceComponent = null;
-                if (TextUtils.isEmpty(serviceComponent)) {
-                    Log.e(TAG, "no selected voice recognition service");
-                    mListener.onHotwordError(ERROR_CLIENT);
-                    return;
-                }
-                serviceIntent.setComponent(ComponentName.unflattenFromString(serviceComponent));
+                Log.e(TAG, "no selected voice recognition service");
+                mListener.onHotwordError(ERROR_CLIENT);
+                return;
             } else {
                 serviceIntent.setComponent(mServiceComponent);
             }
@@ -222,17 +220,15 @@ public class HotwordRecognizer {
                 mListener.onHotwordError(ERROR_CLIENT);
                 return;
             }
+            putMessage(Message.obtain(mHandler, MSG_START));
         } else {
             mListener.onHotwordError(ERROR_SERVICE_ALREADY_STARTED);
             return;
         }
-        putMessage(Message.obtain(mHandler, MSG_START));
     }
 
     /**
-     * Stops recognizing hotword. Please note that
-     * {@link #setRecognitionListener(HotwordRecognitionListener)} should be called beforehand,
-     * otherwise no notifications will be received.
+     * Stops recognizing hotword.
      */
     public void stopRecognition() {
         checkIsCalledFromMainThread();
@@ -245,19 +241,6 @@ public class HotwordRecognizer {
         mServiceComponent = serviceComponent;
     }
 
-    /**
-     * Destroys the {@code HotwordRecognizer} object.
-     */
-    public void destroy() {
-        if (mConnection != null) {
-            mContext.unbindService(mConnection);
-        }
-        mPendingTasks.clear();
-        mService = null;
-        mConnection = null;
-        mListener.mInternalListener = null;
-    }
-
     private void handleStartRecognition() {
         if (!checkOpenConnection()) {
             return;
@@ -271,26 +254,27 @@ public class HotwordRecognizer {
         }
     }
 
-
     private void handleStopRecognition() {
         if (!checkOpenConnection()) {
             return;
         }
         try {
             mService.stopHotwordRecognition(mListener);
+            if (mConnection != null) {
+                mContext.unbindService(mConnection);
+            }
             if (DBG) Log.d(TAG, "service stopRecognition command succeeded");
         } catch (final RemoteException e) {
             Log.e(TAG, "stopRecognition() failed", e);
             mListener.onHotwordError(ERROR_CLIENT);
+        } finally {
+            mPendingTasks.clear();
+            mService = null;
+            mConnection = null;
+            mListener.mInternalListener = null;
         }
     }
 
-    /** changes the listener */
-    private void handleChangeListener(HotwordRecognitionListener listener) {
-        if (DBG) Log.d(TAG, "handleChangeListener, listener=" + listener);
-        mListener.mInternalListener = listener;
-    }
-
     private boolean checkOpenConnection() {
         if (mService != null) {
             return true;