1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.accessibilityservice.cts;
18 
19 import android.accessibility.cts.common.InstrumentedAccessibilityService;
20 import android.accessibilityservice.AccessibilityService;
21 import android.accessibilityservice.InputMethod;
22 import android.view.inputmethod.EditorInfo;
23 
24 import androidx.annotation.MainThread;
25 import androidx.annotation.NonNull;
26 import androidx.annotation.Nullable;
27 
28 import java.util.concurrent.CountDownLatch;
29 
30 /**
31  * A stub accessibility service to install for testing IME APIs
32  */
33 public class StubImeAccessibilityService extends InstrumentedAccessibilityService {
34     private static final int INVALID = -11;
35     public int selStart = -1;
36     public int selEnd = -1;
37     public int oldSelStart = -1;
38     public int oldSelEnd = -1;
39     private CountDownLatch mStartInputLatch = null;
40     private CountDownLatch mSelectionChangeLatch = null;
41     private int mSelectionTarget = INVALID;
42 
43     @FunctionalInterface
44     public interface OnStartInputCallback {
onStartInput(EditorInfo editorInfo, boolean restarting)45         void onStartInput(EditorInfo editorInfo, boolean restarting);
46     }
47 
48     private OnStartInputCallback mOnStartInputCallback;
49 
setOnStartInputCallback(OnStartInputCallback callback)50     public void setOnStartInputCallback(OnStartInputCallback callback) {
51         mOnStartInputCallback = callback;
52     }
53 
54     @FunctionalInterface
55     public interface OnUpdateSelectionCallback {
onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)56         void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
57                 int candidatesStart, int candidatesEnd);
58     }
59 
60     @Nullable
61     private OnUpdateSelectionCallback mOnUpdateSelectionCallback = null;
62 
63     @MainThread
setOnUpdateSelectionCallback(@ullable OnUpdateSelectionCallback listener)64     public void setOnUpdateSelectionCallback(@Nullable OnUpdateSelectionCallback listener) {
65         mOnUpdateSelectionCallback = listener;
66     }
67 
setStartInputCountDownLatch(CountDownLatch latch)68     public void setStartInputCountDownLatch(CountDownLatch latch) {
69         mStartInputLatch = latch;
70     }
71 
setSelectionChangeLatch(CountDownLatch latch)72     public void setSelectionChangeLatch(CountDownLatch latch) {
73         mSelectionChangeLatch = latch;
74     }
setSelectionTarget(int target)75     public void setSelectionTarget(int target) {
76         mSelectionTarget = target;
77     }
78 
79     class InputMethodImpl extends InputMethod {
InputMethodImpl(@onNull AccessibilityService service)80         InputMethodImpl(@NonNull AccessibilityService service) {
81             super(service);
82         }
83 
84         @Override
onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)85         public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart,
86                 int newSelEnd, int candidatesStart, int candidatesEnd) {
87             super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart,
88                     candidatesEnd);
89             // A11y receive placeholder notification.
90             if ((mSelectionChangeLatch != null) && (newSelStart == mSelectionTarget)) {
91                 StubImeAccessibilityService.this.oldSelStart = oldSelStart;
92                 StubImeAccessibilityService.this.oldSelEnd = oldSelEnd;
93                 StubImeAccessibilityService.this.selStart = newSelStart;
94                 StubImeAccessibilityService.this.selEnd = newSelEnd;
95                 mSelectionChangeLatch.countDown();
96                 mSelectionChangeLatch = null;
97             }
98             if (mOnUpdateSelectionCallback != null) {
99                 mOnUpdateSelectionCallback.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart,
100                         newSelEnd, candidatesStart, candidatesEnd);
101             }
102         }
103 
104         @Override
onStartInput(EditorInfo attribute, boolean restarting)105         public void onStartInput(EditorInfo attribute, boolean restarting) {
106             super.onStartInput(attribute, restarting);
107             if (mStartInputLatch != null) {
108                 mStartInputLatch.countDown();
109                 mStartInputLatch = null;
110             }
111             if (mOnStartInputCallback != null) {
112                 mOnStartInputCallback.onStartInput(attribute, restarting);
113             }
114         }
115     }
116 
117     @Override
onCreateInputMethod()118     public InputMethod onCreateInputMethod() {
119         return new InputMethodImpl(this);
120     }
121 }
122