summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2015-07-27 18:11:14 -0700
committerDianne Hackborn <hackbod@google.com>2015-07-28 12:52:11 -0700
commit57dd737443a174379eb638450e4888500d8e4a23 (patch)
treeac0750125c23a5e1e932e93407b421b5c397775a /core
parente827c2525a6b76c130d2b877fbcdcf62b0ce42eb (diff)
downloadframeworks_base-57dd737443a174379eb638450e4888500d8e4a23.zip
frameworks_base-57dd737443a174379eb638450e4888500d8e4a23.tar.gz
frameworks_base-57dd737443a174379eb638450e4888500d8e4a23.tar.bz2
Work on issue #21516866: Implement voice interaction in ResolverActivity
The main change here is to not allow the dialog to go in to its "focus on the last app the user selected" when running in voice interaction mode, instead just always giving a simple list. This also fixes some problems with cleaning up active commands when an activity finishes and not forcing the current session to go away when the screen is turned off. Also added some debug help, having activity print the state of the voice interactor. Change-Id: Ifebee9c74d78398a730a280bb4970f47789dadf5
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/Activity.java8
-rw-r--r--core/java/android/app/VoiceInteractor.java166
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java117
-rw-r--r--core/java/android/service/voice/VoiceInteractionSessionService.java13
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java9
5 files changed, 309 insertions, 4 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e49acfa..bdea608 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1864,7 +1864,10 @@ public class Activity extends ContextThemeWrapper
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
- nci.voiceInteractor = mVoiceInteractor;
+ if (mVoiceInteractor != null) {
+ mVoiceInteractor.retainInstance();
+ nci.voiceInteractor = mVoiceInteractor;
+ }
return nci;
}
@@ -5547,6 +5550,9 @@ public class Activity extends ContextThemeWrapper
mFragments.dumpLoaders(innerPrefix, fd, writer, args);
mFragments.getFragmentManager().dump(innerPrefix, fd, writer, args);
+ if (mVoiceInteractor != null) {
+ mVoiceInteractor.dump(innerPrefix, fd, writer, args);
+ }
if (getWindow() != null &&
getWindow().peekDecorView() != null &&
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index bf7458c..823c427 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -27,6 +27,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.DebugUtils;
import android.util.Log;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractorCallback;
@@ -34,6 +35,8 @@ import com.android.internal.app.IVoiceInteractorRequest;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
/**
@@ -68,6 +71,7 @@ public final class VoiceInteractor {
Context mContext;
Activity mActivity;
+ boolean mRetaining;
final HandlerCaller mHandlerCaller;
final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
@@ -272,6 +276,29 @@ public final class VoiceInteractor {
public void onDetached() {
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ DebugUtils.buildShortClassTag(this, sb);
+ sb.append(" ");
+ sb.append(getRequestTypeName());
+ sb.append(" name=");
+ sb.append(mName);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.print(prefix); writer.print("mRequestInterface=");
+ writer.println(mRequestInterface.asBinder());
+ writer.print(prefix); writer.print("mActivity="); writer.println(mActivity);
+ writer.print(prefix); writer.print("mName="); writer.println(mName);
+ }
+
+ String getRequestTypeName() {
+ return "Request";
+ }
+
void clear() {
mRequestInterface = null;
mContext = null;
@@ -333,6 +360,18 @@ public final class VoiceInteractor {
public void onConfirmationResult(boolean confirmed, Bundle result) {
}
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mPrompt="); writer.println(mPrompt);
+ if (mExtras != null) {
+ writer.print(prefix); writer.print("mExtras="); writer.println(mExtras);
+ }
+ }
+
+ String getRequestTypeName() {
+ return "Confirmation";
+ }
+
IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
IVoiceInteractorCallback callback) throws RemoteException {
return interactor.startConfirmation(packageName, callback, mPrompt, mExtras);
@@ -515,6 +554,38 @@ public final class VoiceInteractor {
public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
}
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mPrompt="); writer.println(mPrompt);
+ if (mOptions != null) {
+ writer.print(prefix); writer.println("Options:");
+ for (int i=0; i<mOptions.length; i++) {
+ Option op = mOptions[i];
+ writer.print(prefix); writer.print(" #"); writer.print(i); writer.println(":");
+ writer.print(prefix); writer.print(" mLabel="); writer.println(op.mLabel);
+ writer.print(prefix); writer.print(" mIndex="); writer.println(op.mIndex);
+ if (op.mSynonyms != null && op.mSynonyms.size() > 0) {
+ writer.print(prefix); writer.println(" Synonyms:");
+ for (int j=0; j<op.mSynonyms.size(); j++) {
+ writer.print(prefix); writer.print(" #"); writer.print(j);
+ writer.print(": "); writer.println(op.mSynonyms.get(j));
+ }
+ }
+ if (op.mExtras != null) {
+ writer.print(prefix); writer.print(" mExtras=");
+ writer.println(op.mExtras);
+ }
+ }
+ }
+ if (mExtras != null) {
+ writer.print(prefix); writer.print("mExtras="); writer.println(mExtras);
+ }
+ }
+
+ String getRequestTypeName() {
+ return "PickOption";
+ }
+
IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
IVoiceInteractorCallback callback) throws RemoteException {
return interactor.startPickOption(packageName, callback, mPrompt, mOptions, mExtras);
@@ -560,6 +631,18 @@ public final class VoiceInteractor {
public void onCompleteResult(Bundle result) {
}
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mPrompt="); writer.println(mPrompt);
+ if (mExtras != null) {
+ writer.print(prefix); writer.print("mExtras="); writer.println(mExtras);
+ }
+ }
+
+ String getRequestTypeName() {
+ return "CompleteVoice";
+ }
+
IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
IVoiceInteractorCallback callback) throws RemoteException {
return interactor.startCompleteVoice(packageName, callback, mPrompt, mExtras);
@@ -607,6 +690,18 @@ public final class VoiceInteractor {
public void onAbortResult(Bundle result) {
}
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mPrompt="); writer.println(mPrompt);
+ if (mExtras != null) {
+ writer.print(prefix); writer.print("mExtras="); writer.println(mExtras);
+ }
+ }
+
+ String getRequestTypeName() {
+ return "AbortVoice";
+ }
+
IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
IVoiceInteractorCallback callback) throws RemoteException {
return interactor.startAbortVoice(packageName, callback, mPrompt, mExtras);
@@ -650,6 +745,18 @@ public final class VoiceInteractor {
public void onCommandResult(boolean isCompleted, Bundle result) {
}
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mCommand="); writer.println(mCommand);
+ if (mArgs != null) {
+ writer.print(prefix); writer.print("mArgs="); writer.println(mArgs);
+ }
+ }
+
+ String getRequestTypeName() {
+ return "Command";
+ }
+
IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
IVoiceInteractorCallback callback) throws RemoteException {
return interactor.startCommand(packageName, callback, mCommand, mArgs);
@@ -721,6 +828,30 @@ public final class VoiceInteractor {
return mVisualPrompt;
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ DebugUtils.buildShortClassTag(this, sb);
+ if (mVisualPrompt != null && mVoicePrompts != null && mVoicePrompts.length == 1
+ && mVisualPrompt.equals(mVoicePrompts[0])) {
+ sb.append(" ");
+ sb.append(mVisualPrompt);
+ } else {
+ if (mVisualPrompt != null) {
+ sb.append(" visual="); sb.append(mVisualPrompt);
+ }
+ if (mVoicePrompts != null) {
+ sb.append(", voice=");
+ for (int i=0; i<mVoicePrompts.length; i++) {
+ if (i > 0) sb.append(" | ");
+ sb.append(mVoicePrompts[i]);
+ }
+ }
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
/** Constructor to support Parcelable behavior. */
Prompt(Parcel in) {
mVoicePrompts = in.readCharSequenceArray();
@@ -773,7 +904,7 @@ public final class VoiceInteractor {
if (N < 1) {
return null;
}
- ArrayList<Request> list = new ArrayList<Request>(N);
+ ArrayList<Request> list = new ArrayList<>(N);
for (int i=0; i<N; i++) {
list.add(mActiveRequests.valueAt(i));
}
@@ -781,6 +912,7 @@ public final class VoiceInteractor {
}
void attachActivity(Activity activity) {
+ mRetaining = false;
if (mActivity == activity) {
return;
}
@@ -797,6 +929,10 @@ public final class VoiceInteractor {
}
}
+ void retainInstance() {
+ mRetaining = true;
+ }
+
void detachActivity() {
ArrayList<Request> reqs = makeRequestList();
if (reqs != null) {
@@ -807,6 +943,16 @@ public final class VoiceInteractor {
req.mContext = null;
}
}
+ if (!mRetaining) {
+ reqs = makeRequestList();
+ if (reqs != null) {
+ for (int i=0; i<reqs.size(); i++) {
+ Request req = reqs.get(i);
+ req.cancel();
+ }
+ }
+ mActiveRequests.clear();
+ }
mContext = null;
mActivity = null;
}
@@ -902,4 +1048,22 @@ public final class VoiceInteractor {
throw new RuntimeException("Voice interactor has died", e);
}
}
+
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ String innerPrefix = prefix + " ";
+ if (mActiveRequests.size() > 0) {
+ writer.print(prefix); writer.println("Active voice requests:");
+ for (int i=0; i<mActiveRequests.size(); i++) {
+ Request req = mActiveRequests.valueAt(i);
+ writer.print(prefix); writer.print(" #"); writer.print(i);
+ writer.print(": ");
+ writer.println(req);
+ req.dump(innerPrefix, fd, writer, args);
+ }
+ }
+ writer.print(prefix); writer.println("VoiceInteractor misc state:");
+ writer.print(prefix); writer.print(" mInteractor=");
+ writer.println(mInteractor.asBinder());
+ writer.print(prefix); writer.print(" mActivity="); writer.println(mActivity);
+ }
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index f647aa6..ec14740 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -37,7 +37,9 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.DebugUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -55,6 +57,8 @@ import com.android.internal.app.IVoiceInteractorRequest;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -369,6 +373,34 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
} catch (RemoteException e) {
}
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ DebugUtils.buildShortClassTag(this, sb);
+ sb.append(" ");
+ sb.append(mInterface.asBinder());
+ sb.append(" pkg=");
+ sb.append(mCallingPackage);
+ sb.append(" uid=");
+ UserHandle.formatUid(sb, mCallingUid);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.print(prefix); writer.print("mInterface=");
+ writer.println(mInterface.asBinder());
+ writer.print(prefix); writer.print("mCallingPackage="); writer.print(mCallingPackage);
+ writer.print(" mCallingUid="); UserHandle.formatUid(writer, mCallingUid);
+ writer.println();
+ writer.print(prefix); writer.print("mCallback=");
+ writer.println(mCallback.asBinder());
+ if (mExtras != null) {
+ writer.print(prefix); writer.print("mExtras=");
+ writer.println(mExtras);
+ }
+ }
}
/**
@@ -422,6 +454,12 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
} catch (RemoteException e) {
}
}
+
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mPrompt=");
+ writer.println(mPrompt);
+ }
}
/**
@@ -504,6 +542,34 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
sendPickOptionResult(true, selections, result);
}
+
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mPrompt=");
+ writer.println(mPrompt);
+ if (mOptions != null) {
+ writer.print(prefix); writer.println("Options:");
+ for (int i=0; i<mOptions.length; i++) {
+ VoiceInteractor.PickOptionRequest.Option op = mOptions[i];
+ writer.print(prefix); writer.print(" #"); writer.print(i); writer.println(":");
+ writer.print(prefix); writer.print(" mLabel=");
+ writer.println(op.getLabel());
+ writer.print(prefix); writer.print(" mIndex=");
+ writer.println(op.getIndex());
+ if (op.countSynonyms() > 0) {
+ writer.print(prefix); writer.println(" Synonyms:");
+ for (int j=0; j<op.countSynonyms(); j++) {
+ writer.print(prefix); writer.print(" #"); writer.print(j);
+ writer.print(": "); writer.println(op.getSynonymAt(j));
+ }
+ }
+ if (op.getExtras() != null) {
+ writer.print(prefix); writer.print(" mExtras=");
+ writer.println(op.getExtras());
+ }
+ }
+ }
+ }
}
/**
@@ -557,6 +623,12 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
} catch (RemoteException e) {
}
}
+
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mPrompt=");
+ writer.println(mPrompt);
+ }
}
/**
@@ -607,6 +679,12 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
} catch (RemoteException e) {
}
}
+
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mPrompt=");
+ writer.println(mPrompt);
+ }
}
/**
@@ -661,6 +739,12 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
public void sendResult(Bundle result) {
sendCommandResult(true, result);
}
+
+ void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.print(prefix); writer.print("mCommand=");
+ writer.println(mCommand);
+ }
}
static final int MSG_START_CONFIRMATION = 1;
@@ -1446,4 +1530,37 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
*/
public void onCancelRequest(Request request) {
}
+
+ /**
+ * Print the Service's state into the given stream. This gets invoked by
+ * {@link VoiceInteractionSessionService} when its Service
+ * {@link android.app.Service#dump} method is called.
+ *
+ * @param prefix Text to print at the front of each line.
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param writer The PrintWriter to which you should dump your state. This will be
+ * closed for you after you return.
+ * @param args additional arguments to the dump request.
+ */
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.print(prefix); writer.print("mToken="); writer.println(mToken);
+ writer.print(prefix); writer.print("mTheme=#"); writer.println(Integer.toHexString(mTheme));
+ writer.print(prefix); writer.print("mInitialized="); writer.println(mInitialized);
+ writer.print(prefix); writer.print("mWindowAdded="); writer.print(mWindowAdded);
+ writer.print(" mWindowVisible="); writer.println(mWindowVisible);
+ writer.print(prefix); writer.print("mWindowWasVisible="); writer.print(mWindowWasVisible);
+ writer.print(" mInShowWindow="); writer.println(mInShowWindow);
+ if (mActiveRequests.size() > 0) {
+ writer.print(prefix); writer.println("Active requests:");
+ String innerPrefix = prefix + " ";
+ for (int i=0; i<mActiveRequests.size(); i++) {
+ Request req = mActiveRequests.valueAt(i);
+ writer.print(prefix); writer.print(" #"); writer.print(i);
+ writer.print(": ");
+ writer.println(req);
+ req.dump(innerPrefix, fd, writer, args);
+
+ }
+ }
+ }
}
diff --git a/core/java/android/service/voice/VoiceInteractionSessionService.java b/core/java/android/service/voice/VoiceInteractionSessionService.java
index fb9f973..424ff9d 100644
--- a/core/java/android/service/voice/VoiceInteractionSessionService.java
+++ b/core/java/android/service/voice/VoiceInteractionSessionService.java
@@ -30,6 +30,9 @@ import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* An active voice interaction session, initiated by a {@link VoiceInteractionService}.
*/
@@ -101,6 +104,16 @@ public abstract class VoiceInteractionSessionService extends Service {
}
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (mSession == null) {
+ writer.println("(no active session)");
+ } else {
+ writer.println("VoiceInteractionSession:");
+ mSession.dump(" ", fd, writer, args);
+ }
+ }
+
void doNewSession(IBinder token, Bundle args, int startFlags) {
if (mSession != null) {
mSession.doDestroy();
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 1bf17e6..89599e0 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -500,7 +500,7 @@ public class ResolverActivity extends Activity {
mPackageMonitor.unregister();
mRegistered = false;
}
- if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()) {
// This resolver is in the unusual situation where it has been
// launched at the top of a new task. We don't let it be added
// to the recent tasks shown to the user, and we need to make sure
@@ -810,8 +810,13 @@ public class ResolverActivity extends Activity {
void configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
List<ResolveInfo> rList, boolean alwaysUseOption) {
+ // The last argument of createAdapter is whether to do special handling
+ // of the last used choice to highlight it in the list. We need to always
+ // turn this off when running under voice interaction, since it results in
+ // a more complicated UI that the current voice interaction flow is not able
+ // to handle.
mAdapter = createAdapter(this, payloadIntents, initialIntents, rList,
- mLaunchedFromUid, alwaysUseOption);
+ mLaunchedFromUid, alwaysUseOption && !isVoiceInteraction());
final int layoutId;
if (mAdapter.hasFilteredItem()) {