1193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver/*
2193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * Copyright (C) 2016 The Android Open Source Project
3193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver *
4193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * Licensed under the Apache License, Version 2.0 (the "License");
5193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * you may not use this file except in compliance with the License.
6193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * You may obtain a copy of the License at
7193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver *
8193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver *      http://www.apache.org/licenses/LICENSE-2.0
9193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver *
10193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * Unless required by applicable law or agreed to in writing, software
11193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * distributed under the License is distributed on an "AS IS" BASIS,
12193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * See the License for the specific language governing permissions and
14193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * limitations under the License.
15193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver */
16193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverpackage android.text.style;
17193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
18193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
1923161e7170ec2493ec830d04205f5696be159026Phil Weaverimport static android.view.accessibility.AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID;
2023161e7170ec2493ec830d04205f5696be159026Phil Weaverimport static android.view.accessibility.AccessibilityNodeInfo.UNDEFINED_NODE_ID;
2123161e7170ec2493ec830d04205f5696be159026Phil Weaverimport static android.view.accessibility.AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
22193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
23193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport android.os.Bundle;
24193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport android.os.Parcel;
25193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport android.os.Parcelable;
26193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport android.text.ParcelableSpan;
27193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport android.text.Spanned;
28193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport android.text.TextUtils;
29193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport android.view.View;
3023161e7170ec2493ec830d04205f5696be159026Phil Weaverimport android.view.accessibility.AccessibilityInteractionClient;
31193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport android.view.accessibility.AccessibilityNodeInfo;
32193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
33193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverimport com.android.internal.R;
34193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
35193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver/**
36193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * {@link ClickableSpan} cannot be parceled, but accessibility services need to be able to cause
37193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * their callback handlers to be called. This class serves as a parcelable placeholder for the
38193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * real spans.
39193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver *
40193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * This span is also passed back to an app's process when an accessibility service tries to click
41193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * it. It contains enough information to track down the original clickable span so it can be
42193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * called.
43193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver *
44193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver * @hide
45193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver */
46193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaverpublic class AccessibilityClickableSpan extends ClickableSpan
47193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        implements ParcelableSpan {
48193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    // The id of the span this one replaces
49193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    private final int mOriginalClickableSpanId;
50193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
5123161e7170ec2493ec830d04205f5696be159026Phil Weaver    private int mWindowId = UNDEFINED_WINDOW_ID;
5223161e7170ec2493ec830d04205f5696be159026Phil Weaver    private long mSourceNodeId = UNDEFINED_NODE_ID;
5323161e7170ec2493ec830d04205f5696be159026Phil Weaver    private int mConnectionId = UNDEFINED_CONNECTION_ID;
54193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
55193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    /**
56193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     * @param originalClickableSpanId The id of the span this one replaces
57193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     */
58193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public AccessibilityClickableSpan(int originalClickableSpanId) {
59193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        mOriginalClickableSpanId = originalClickableSpanId;
60193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
61193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
62193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public AccessibilityClickableSpan(Parcel p) {
63193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        mOriginalClickableSpanId = p.readInt();
64193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
65193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
66193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    @Override
67193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public int getSpanTypeId() {
68193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        return getSpanTypeIdInternal();
69193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
70193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
71193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    @Override
72193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public int getSpanTypeIdInternal() {
73193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        return TextUtils.ACCESSIBILITY_CLICKABLE_SPAN;
74193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
75193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
76193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    @Override
77193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public int describeContents() {
78193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        return 0;
79193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
80193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
81193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    @Override
82193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public void writeToParcel(Parcel dest, int flags) {
83193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        writeToParcelInternal(dest, flags);
84193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
85193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
86193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    @Override
87193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public void writeToParcelInternal(Parcel dest, int flags) {
88193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        dest.writeInt(mOriginalClickableSpanId);
89193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
90193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
91193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    /**
92193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     * Find the ClickableSpan that matches the one used to create this object.
93193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     *
94193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     * @param text The text that contains the original ClickableSpan.
95193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     * @return The ClickableSpan that matches this object, or {@code null} if no such object
96193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     * can be found.
97193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     */
98193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public ClickableSpan findClickableSpan(CharSequence text) {
99193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        if (!(text instanceof Spanned)) {
100193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver            return null;
101193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        }
102193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        Spanned sp = (Spanned) text;
103193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        ClickableSpan[] os = sp.getSpans(0, text.length(), ClickableSpan.class);
104193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        for (int i = 0; i < os.length; i++) {
105193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver            if (os[i].getId() == mOriginalClickableSpanId) {
106193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver                return os[i];
107193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver            }
108193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        }
109193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        return null;
110193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
111193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
112193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    /**
11323161e7170ec2493ec830d04205f5696be159026Phil Weaver     * Configure this object to perform clicks on the view that contains the original span.
114193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     *
11523161e7170ec2493ec830d04205f5696be159026Phil Weaver     * @param accessibilityNodeInfo The info corresponding to the view containing the original
11623161e7170ec2493ec830d04205f5696be159026Phil Weaver     *                              span.
117193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     */
11823161e7170ec2493ec830d04205f5696be159026Phil Weaver    public void copyConnectionDataFrom(AccessibilityNodeInfo accessibilityNodeInfo) {
11923161e7170ec2493ec830d04205f5696be159026Phil Weaver        mConnectionId = accessibilityNodeInfo.getConnectionId();
12023161e7170ec2493ec830d04205f5696be159026Phil Weaver        mWindowId = accessibilityNodeInfo.getWindowId();
12123161e7170ec2493ec830d04205f5696be159026Phil Weaver        mSourceNodeId = accessibilityNodeInfo.getSourceNodeId();
122193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
123193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
124193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    /**
125193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     * Perform the click from an accessibility service. Will not work unless
126193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     * setAccessibilityNodeInfo is called with a properly initialized node.
127193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     *
128193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     * @param unused This argument is required by the superclass but is unused. The real view will
129193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     * be determined by the AccessibilityNodeInfo.
130193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver     */
131193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    @Override
132193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public void onClick(View unused) {
133193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        Bundle arguments = new Bundle();
134193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver        arguments.putParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN, this);
135193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
13623161e7170ec2493ec830d04205f5696be159026Phil Weaver        if ((mWindowId == UNDEFINED_WINDOW_ID) || (mSourceNodeId == UNDEFINED_NODE_ID)
13723161e7170ec2493ec830d04205f5696be159026Phil Weaver                || (mConnectionId == UNDEFINED_CONNECTION_ID)) {
13823161e7170ec2493ec830d04205f5696be159026Phil Weaver            throw new RuntimeException(
13923161e7170ec2493ec830d04205f5696be159026Phil Weaver                    "ClickableSpan for accessibility service not properly initialized");
14023161e7170ec2493ec830d04205f5696be159026Phil Weaver        }
14123161e7170ec2493ec830d04205f5696be159026Phil Weaver
14223161e7170ec2493ec830d04205f5696be159026Phil Weaver        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
14323161e7170ec2493ec830d04205f5696be159026Phil Weaver        client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
14423161e7170ec2493ec830d04205f5696be159026Phil Weaver                R.id.accessibilityActionClickOnClickableSpan, arguments);
145193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    }
146193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
147193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver    public static final Parcelable.Creator<AccessibilityClickableSpan> CREATOR =
148193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver            new Parcelable.Creator<AccessibilityClickableSpan>() {
149193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver                @Override
150193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver                public AccessibilityClickableSpan createFromParcel(Parcel parcel) {
151193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver                    return new AccessibilityClickableSpan(parcel);
152193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver                }
153193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver
154193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver                @Override
155193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver                public AccessibilityClickableSpan[] newArray(int size) {
156193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver                    return new AccessibilityClickableSpan[size];
157193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver                }
158193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver            };
159193520e3dff5248ddcf8435203bf99d2ba667219Phil Weaver}
160