diff options
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/app/AssistStructure.java | 138 | ||||
| -rw-r--r-- | core/java/android/service/voice/VoiceInteractionSession.java | 13 | ||||
| -rw-r--r-- | core/java/android/view/ViewAssistStructure.java | 3 |
3 files changed, 141 insertions, 13 deletions
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java index e31c821..1e159a3 100644 --- a/core/java/android/app/AssistStructure.java +++ b/core/java/android/app/AssistStructure.java @@ -20,11 +20,15 @@ import android.content.ComponentName; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; +import android.os.Binder; import android.os.Bundle; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.PooledStringReader; import android.os.PooledStringWriter; +import android.os.RemoteException; +import android.os.SystemClock; import android.text.TextPaint; import android.text.TextUtils; import android.util.Log; @@ -49,12 +53,35 @@ final public class AssistStructure implements Parcelable { */ public static final String ASSIST_KEY = "android:assist_structure"; - final ComponentName mActivityComponent; + boolean mHaveData; + + ComponentName mActivityComponent; final ArrayList<WindowNode> mWindowNodes = new ArrayList<>(); + final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>(); + + SendChannel mSendChannel; + IBinder mReceiveChannel; + Rect mTmpRect = new Rect(); + static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1; + static final String DESCRIPTOR = "android.app.AssistStructure"; + + final class SendChannel extends Binder { + @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + if (code == TRANSACTION_XFER) { + data.enforceInterface(DESCRIPTOR); + writeContentToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + return true; + } else { + return super.onTransact(code, data, reply, flags); + } + } + } + final static class ViewNodeText { CharSequence mText; int mTextSelectionStart; @@ -112,7 +139,7 @@ final public class AssistStructure implements Parcelable { mHeight = rect.height(); mTitle = root.getTitle(); mRoot = new ViewNode(); - ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot); + ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false); view.dispatchProvideAssistStructure(builder); } @@ -425,10 +452,12 @@ final public class AssistStructure implements Parcelable { static class ViewNodeBuilder extends ViewAssistStructure { final AssistStructure mAssist; final ViewNode mNode; + final boolean mAsync; - ViewNodeBuilder(AssistStructure assist, ViewNode node) { + ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) { mAssist = assist; mNode = node; + mAsync = async; } @Override @@ -628,7 +657,32 @@ final public class AssistStructure implements Parcelable { public ViewAssistStructure newChild(int index) { ViewNode node = new ViewNode(); mNode.mChildren[index] = node; - return new ViewNodeBuilder(mAssist, node); + return new ViewNodeBuilder(mAssist, node, false); + } + + @Override + public ViewAssistStructure asyncNewChild(int index) { + synchronized (mAssist) { + ViewNode node = new ViewNode(); + mNode.mChildren[index] = node; + ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true); + mAssist.mPendingAsyncChildren.add(builder); + return builder; + } + } + + @Override + public void asyncCommit() { + synchronized (mAssist) { + if (!mAsync) { + throw new IllegalStateException("Child " + this + + " was not created with ViewAssistStructure.asyncNewChild"); + } + if (!mAssist.mPendingAsyncChildren.remove(this)) { + throw new IllegalStateException("Child " + this + " already committed"); + } + mAssist.notifyAll(); + } } @Override @@ -638,6 +692,7 @@ final public class AssistStructure implements Parcelable { } AssistStructure(Activity activity) { + mHaveData = true; mActivityComponent = activity.getComponentName(); ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews( activity.getActivityToken()); @@ -648,13 +703,7 @@ final public class AssistStructure implements Parcelable { } AssistStructure(Parcel in) { - PooledStringReader preader = new PooledStringReader(in); - mActivityComponent = ComponentName.readFromParcel(in); - final int N = in.readInt(); - for (int i=0; i<N; i++) { - mWindowNodes.add(new WindowNode(in, preader)); - } - //dump(); + mReceiveChannel = in.readStrongBinder(); } /** @hide */ @@ -731,6 +780,7 @@ final public class AssistStructure implements Parcelable { } public ComponentName getActivityComponent() { + ensureData(); return mActivityComponent; } @@ -738,6 +788,7 @@ final public class AssistStructure implements Parcelable { * Return the number of window contents that have been collected in this assist data. */ public int getWindowNodeCount() { + ensureData(); return mWindowNodes.size(); } @@ -746,6 +797,7 @@ final public class AssistStructure implements Parcelable { * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1. */ public WindowNode getWindowNodeAt(int index) { + ensureData(); return mWindowNodes.get(index); } @@ -753,11 +805,47 @@ final public class AssistStructure implements Parcelable { return 0; } - public void writeToParcel(Parcel out, int flags) { + /** @hide */ + public void ensureData() { + if (mHaveData) { + return; + } + mHaveData = true; + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(DESCRIPTOR); + try { + mReceiveChannel.transact(TRANSACTION_XFER, data, reply, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failure reading AssistStructure data", e); + return; + } + readContentFromParcel(reply); + data.recycle(); + reply.recycle(); + } + + void writeContentToParcel(Parcel out, int flags) { + // First make sure all content has been created. + boolean skipStructure = false; + synchronized (this) { + long endTime = SystemClock.uptimeMillis() + 5000; + long now; + while (mPendingAsyncChildren.size() > 0 && (now=SystemClock.uptimeMillis()) < endTime) { + try { + wait(endTime-now); + } catch (InterruptedException e) { + } + } + if (mPendingAsyncChildren.size() > 0) { + // We waited too long, assume none of the assist structure is valid. + skipStructure = true; + } + } int start = out.dataPosition(); PooledStringWriter pwriter = new PooledStringWriter(out); ComponentName.writeToParcel(mActivityComponent, out); - final int N = mWindowNodes.size(); + final int N = skipStructure ? 0 : mWindowNodes.size(); out.writeInt(N); for (int i=0; i<N; i++) { mWindowNodes.get(i).writeToParcel(out, pwriter); @@ -766,6 +854,30 @@ final public class AssistStructure implements Parcelable { Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes"); } + void readContentFromParcel(Parcel in) { + PooledStringReader preader = new PooledStringReader(in); + mActivityComponent = ComponentName.readFromParcel(in); + final int N = in.readInt(); + for (int i=0; i<N; i++) { + mWindowNodes.add(new WindowNode(in, preader)); + } + //dump(); + } + + public void writeToParcel(Parcel out, int flags) { + if (mHaveData) { + // This object holds its data. We want to write a send channel that the + // other side can use to retrieve that data. + if (mSendChannel == null) { + mSendChannel = new SendChannel(); + } + out.writeStrongBinder(mSendChannel); + } else { + // This object doesn't hold its data, so just propagate along its receive channel. + out.writeStrongBinder(mReceiveChannel); + } + } + public static final Parcelable.Creator<AssistStructure> CREATOR = new Parcelable.Creator<AssistStructure>() { public AssistStructure createFromParcel(Parcel in) { diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 3245f55..49ab797 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -16,6 +16,7 @@ package android.service.voice; +import android.app.AssistStructure; import android.app.Dialog; import android.app.Instrumentation; import android.app.VoiceInteractor; @@ -175,6 +176,18 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { @Override public void handleAssist(Bundle assistBundle) { + // We want to pre-warm the AssistStructure before handing it off to the main + // thread. There is a strong argument to be made that it should be handed + // through as a separate param rather than part of the assistBundle. + if (assistBundle != null) { + Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT); + if (assistContext != null) { + AssistStructure as = AssistStructure.getAssistStructure(assistContext); + if (as != null) { + as.ensureData(); + } + } + } mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_ASSIST, assistBundle)); } diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java index c05ed6f..7d263c5 100644 --- a/core/java/android/view/ViewAssistStructure.java +++ b/core/java/android/view/ViewAssistStructure.java @@ -73,6 +73,9 @@ public abstract class ViewAssistStructure { public abstract int getChildCount(); public abstract ViewAssistStructure newChild(int index); + public abstract ViewAssistStructure asyncNewChild(int index); + public abstract void asyncCommit(); + /** @hide */ public abstract Rect getTempRect(); } |
