All Downloads are FREE. Search and download functionalities are using the official Maven repository.

android.app.assist.AssistStructure Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: Q-robolectric-5415296
Show newest version
package android.app.assist;

import android.app.Activity;
import android.content.ComponentName;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.BadParcelableException;
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.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewStructure;
import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;

import java.util.ArrayList;

/**
 * Assist data automatically created by the platform's implementation
 * of {@link android.app.Activity#onProvideAssistData}.
 */
public class AssistStructure implements Parcelable {
    static final String TAG = "AssistStructure";

    static final boolean DEBUG_PARCEL = false;
    static final boolean DEBUG_PARCEL_CHILDREN = false;
    static final boolean DEBUG_PARCEL_TREE = false;

    static final int VALIDATE_WINDOW_TOKEN = 0x11111111;
    static final int VALIDATE_VIEW_TOKEN = 0x22222222;

    boolean mHaveData;

    ComponentName mActivityComponent;

    final ArrayList mWindowNodes = new ArrayList<>();

    final ArrayList 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 static class SendChannel extends Binder {
        volatile AssistStructure mAssistStructure;

        SendChannel(AssistStructure as) {
            mAssistStructure = as;
        }

        @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            if (code == TRANSACTION_XFER) {
                AssistStructure as = mAssistStructure;
                if (as == null) {
                    return true;
                }

                data.enforceInterface(DESCRIPTOR);
                IBinder token = data.readStrongBinder();
                if (DEBUG_PARCEL) Log.d(TAG, "Request for data on " + as
                        + " using token " + token);
                if (token != null) {
                    if (DEBUG_PARCEL) Log.d(TAG, "Resuming partial write of " + token);
                    if (token instanceof ParcelTransferWriter) {
                        ParcelTransferWriter xfer = (ParcelTransferWriter)token;
                        xfer.writeToParcel(as, reply);
                        return true;
                    }
                    Log.w(TAG, "Caller supplied bad token type: " + token);
                    // Don't write anything; this is the end of the data.
                    return true;
                }
                //long start = SystemClock.uptimeMillis();
                ParcelTransferWriter xfer = new ParcelTransferWriter(as, reply);
                xfer.writeToParcel(as, reply);
                //Log.i(TAG, "Time to parcel: " + (SystemClock.uptimeMillis()-start) + "ms");
                return true;
            } else {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }

    final static class ViewStackEntry {
        ViewNode node;
        int curChild;
        int numChildren;
    }

    final static class ParcelTransferWriter extends Binder {
        final boolean mWriteStructure;
        int mCurWindow;
        int mNumWindows;
        final ArrayList mViewStack = new ArrayList<>();
        ViewStackEntry mCurViewStackEntry;
        int mCurViewStackPos;
        int mNumWrittenWindows;
        int mNumWrittenViews;
        final float[] mTmpMatrix = new float[9];

        ParcelTransferWriter(AssistStructure as, Parcel out) {
            mWriteStructure = as.waitForReady();
            ComponentName.writeToParcel(as.mActivityComponent, out);
            mNumWindows = as.mWindowNodes.size();
            if (mWriteStructure && mNumWindows > 0) {
                out.writeInt(mNumWindows);
            } else {
                out.writeInt(0);
            }
        }

        void writeToParcel(AssistStructure as, Parcel out) {
            int start = out.dataPosition();
            mNumWrittenWindows = 0;
            mNumWrittenViews = 0;
            boolean more = writeToParcelInner(as, out);
            Log.i(TAG, "Flattened " + (more ? "partial" : "final") + " assist data: "
                    + (out.dataPosition() - start)
                    + " bytes, containing " + mNumWrittenWindows + " windows, "
                    + mNumWrittenViews + " views");
        }

        boolean writeToParcelInner(AssistStructure as, Parcel out) {
            if (mNumWindows == 0) {
                return false;
            }
            if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringWriter @ " + out.dataPosition());
            PooledStringWriter pwriter = new PooledStringWriter(out);
            while (writeNextEntryToParcel(as, out, pwriter)) {
                // If the parcel is above the IPC limit, then we are getting too
                // large for a single IPC so stop here and let the caller come back when it
                // is ready for more.
                if (out.dataSize() > IBinder.MAX_IPC_SIZE) {
                    if (DEBUG_PARCEL) Log.d(TAG, "Assist data size is " + out.dataSize()
                            + " @ pos " + out.dataPosition() + "; returning partial result");
                    out.writeInt(0);
                    out.writeStrongBinder(this);
                    if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
                            + out.dataPosition() + ", size " + pwriter.getStringCount());
                    pwriter.finish();
                    return true;
                }
            }
            if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
                    + out.dataPosition() + ", size " + pwriter.getStringCount());
            pwriter.finish();
            mViewStack.clear();
            return false;
        }

        void pushViewStackEntry(ViewNode node, int pos) {
            ViewStackEntry entry;
            if (pos >= mViewStack.size()) {
                entry = new ViewStackEntry();
                mViewStack.add(entry);
                if (DEBUG_PARCEL_TREE) Log.d(TAG, "New stack entry at " + pos + ": " + entry);
            } else {
                entry = mViewStack.get(pos);
                if (DEBUG_PARCEL_TREE) Log.d(TAG, "Existing stack entry at " + pos + ": " + entry);
            }
            entry.node = node;
            entry.numChildren = node.getChildCount();
            entry.curChild = 0;
            mCurViewStackEntry = entry;
        }

        void writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj) {
            if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition()
                    + ", windows=" + mNumWrittenWindows
                    + ", views=" + mNumWrittenViews
                    + ", level=" + (mCurViewStackPos+levelAdj));
            out.writeInt(VALIDATE_VIEW_TOKEN);
            int flags = child.writeSelfToParcel(out, pwriter, mTmpMatrix);
            mNumWrittenViews++;
            // If the child has children, push it on the stack to write them next.
            if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) {
                if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
                        "Preparing to write " + child.mChildren.length
                                + " children: @ #" + mNumWrittenViews
                                + ", level " + (mCurViewStackPos+levelAdj));
                out.writeInt(child.mChildren.length);
                int pos = ++mCurViewStackPos;
                pushViewStackEntry(child, pos);
            }
        }

        boolean writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter) {
            // Write next view node if appropriate.
            if (mCurViewStackEntry != null) {
                if (mCurViewStackEntry.curChild < mCurViewStackEntry.numChildren) {
                    // Write the next child in the current view.
                    if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing child #"
                            + mCurViewStackEntry.curChild + " in " + mCurViewStackEntry.node);
                    ViewNode child = mCurViewStackEntry.node.mChildren[mCurViewStackEntry.curChild];
                    mCurViewStackEntry.curChild++;
                    writeView(child, out, pwriter, 1);
                    return true;
                }

                // We are done writing children of the current view; pop off the stack.
                do {
                    int pos = --mCurViewStackPos;
                    if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with " + mCurViewStackEntry.node
                            + "; popping up to " + pos);
                    if (pos < 0) {
                        // Reached the last view; step to next window.
                        if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with view hierarchy!");
                        mCurViewStackEntry = null;
                        break;
                    }
                    mCurViewStackEntry = mViewStack.get(pos);
                } while (mCurViewStackEntry.curChild >= mCurViewStackEntry.numChildren);
                return true;
            }

            // Write the next window if appropriate.
            int pos = mCurWindow;
            if (pos < mNumWindows) {
                WindowNode win = as.mWindowNodes.get(pos);
                mCurWindow++;
                if (DEBUG_PARCEL) Log.d(TAG, "write window #" + pos + ": at " + out.dataPosition()
                        + ", windows=" + mNumWrittenWindows
                        + ", views=" + mNumWrittenViews);
                out.writeInt(VALIDATE_WINDOW_TOKEN);
                win.writeSelfToParcel(out, pwriter, mTmpMatrix);
                mNumWrittenWindows++;
                ViewNode root = win.mRoot;
                mCurViewStackPos = 0;
                if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing initial root view " + root);
                writeView(root, out, pwriter, 0);
                return true;
            }

            return false;
        }
    }

    final class ParcelTransferReader {
        final float[] mTmpMatrix = new float[9];
        PooledStringReader mStringReader;

        int mNumReadWindows;
        int mNumReadViews;

        private final IBinder mChannel;
        private IBinder mTransferToken;
        private Parcel mCurParcel;

        ParcelTransferReader(IBinder channel) {
            mChannel = channel;
        }

        void go() {
            fetchData();
            mActivityComponent = ComponentName.readFromParcel(mCurParcel);
            final int N = mCurParcel.readInt();
            if (N > 0) {
                if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
                        + mCurParcel.dataPosition());
                mStringReader = new PooledStringReader(mCurParcel);
                if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
                        + mStringReader.getStringCount());
                for (int i=0; i>16)&0x7fff;
                val = in.readInt();
                mWidth = val&0x7fff;
                mHeight = (val>>16)&0x7fff;
            }
            if ((flags&FLAGS_HAS_SCROLL) != 0) {
                mScrollX = in.readInt();
                mScrollY = in.readInt();
            }
            if ((flags&FLAGS_HAS_MATRIX) != 0) {
                mMatrix = new Matrix();
                in.readFloatArray(reader.mTmpMatrix);
                mMatrix.setValues(reader.mTmpMatrix);
            }
            if ((flags&FLAGS_HAS_ELEVATION) != 0) {
                mElevation = in.readFloat();
            }
            if ((flags&FLAGS_HAS_ALPHA) != 0) {
                mAlpha = in.readFloat();
            }
            if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
                mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
            }
            if ((flags&FLAGS_HAS_TEXT) != 0) {
                mText = new ViewNodeText(in, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
            }
            if ((flags&FLAGS_HAS_EXTRAS) != 0) {
                mExtras = in.readBundle();
            }
            if ((flags&FLAGS_HAS_CHILDREN) != 0) {
                final int NCHILDREN = in.readInt();
                if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
                        "Preparing to read " + NCHILDREN
                                + " children: @ #" + reader.mNumReadViews
                                + ", level " + nestingLevel);
                mChildren = new ViewNode[NCHILDREN];
                for (int i=0; i views = WindowManagerGlobal.getInstance().getRootViews(
                activity.getActivityToken());
        for (int i=0; i 0) {
            Log.i(TAG, prefix + "  Children:");
            String cprefix = prefix + "    ";
            for (int i=0; i 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.
                Log.w(TAG, "Skipping assist structure, waiting too long for async children (have "
                        + mPendingAsyncChildren.size() + " remaining");
                skipStructure = true;
            }
        }
        return !skipStructure;
    }

    /** @hide */
    public void clearSendChannel() {
        if (mSendChannel != null) {
            mSendChannel.mAssistStructure = null;
        }
    }

    public int describeContents() {
        return 0;
    }

    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(this);
            }
            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 CREATOR
            = new Parcelable.Creator() {
        public AssistStructure createFromParcel(Parcel in) {
            return new AssistStructure(in);
        }

        public AssistStructure[] newArray(int size) {
            return new AssistStructure[size];
        }
    };
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy