• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.apps.inputmethod.simpleime.ims;
18 
19 import android.content.res.Configuration;
20 import android.inputmethodservice.InputMethodService;
21 import android.util.Log;
22 import android.view.inputmethod.EditorInfo;
23 
24 import androidx.annotation.IntDef;
25 import androidx.annotation.NonNull;
26 import androidx.annotation.Nullable;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.lang.ref.WeakReference;
31 import java.util.concurrent.CountDownLatch;
32 
33 /** Wrapper of {@link InputMethodService} to expose interfaces for testing purpose. */
34 public class InputMethodServiceWrapper extends InputMethodService {
35 
36     private static final String TAG = "InputMethodServiceWrapper";
37 
38     /** Last created instance of this wrapper. */
39     @NonNull
40     private static WeakReference<InputMethodServiceWrapper> sInstance = new WeakReference<>(null);
41 
42     /** IME show event ({@link #onStartInputView}). */
43     public static final int EVENT_SHOW = 0;
44 
45     /** IME hide event ({@link #onFinishInputView}). */
46     public static final int EVENT_HIDE = 1;
47 
48     /** IME configuration change event ({@link #onConfigurationChanged}). */
49     public static final int EVENT_CONFIG = 2;
50 
51     /** The type of event that can be waited with a latch. */
52     @IntDef(value = {
53             EVENT_SHOW,
54             EVENT_HIDE,
55             EVENT_CONFIG,
56     })
57     @Retention(RetentionPolicy.SOURCE)
58     public @interface Event {}
59 
60     /** The IME event type that the current latch, if any, waits on. */
61     @Event
62     private int mLatchEvent;
63 
64     private boolean mInputViewStarted;
65 
66     /**
67      * @see #setCountDownLatchForTesting
68      */
69     @Nullable
70     private CountDownLatch mCountDownLatch;
71 
72     @Override
onCreate()73     public void onCreate() {
74         Log.i(TAG, "onCreate()");
75         super.onCreate();
76         sInstance = new WeakReference<>(this);
77     }
78 
79     @Override
onStartInput(EditorInfo info, boolean restarting)80     public void onStartInput(EditorInfo info, boolean restarting) {
81         Log.i(TAG, "onStartInput() editor=" + dumpEditorInfo(info) + ", restarting=" + restarting);
82         super.onStartInput(info, restarting);
83     }
84 
85     @Override
onFinishInput()86     public void onFinishInput() {
87         Log.i(TAG, "onFinishInput()");
88         super.onFinishInput();
89     }
90 
91     @Override
onStartInputView(EditorInfo info, boolean restarting)92     public void onStartInputView(EditorInfo info, boolean restarting) {
93         Log.i(TAG, "onStartInputView() editor=" + dumpEditorInfo(info)
94                 + ", restarting=" + restarting);
95         super.onStartInputView(info, restarting);
96         mInputViewStarted = true;
97         if (mCountDownLatch != null && mLatchEvent == EVENT_SHOW) {
98             mCountDownLatch.countDown();
99         }
100     }
101 
102     @Override
onFinishInputView(boolean finishingInput)103     public void onFinishInputView(boolean finishingInput) {
104         Log.i(TAG, "onFinishInputView()");
105         super.onFinishInputView(finishingInput);
106         mInputViewStarted = false;
107 
108         if (mCountDownLatch != null && mLatchEvent == EVENT_HIDE) {
109             mCountDownLatch.countDown();
110         }
111     }
112 
113     @Override
onConfigurationChanged(Configuration newConfig)114     public void onConfigurationChanged(Configuration newConfig) {
115         Log.i(TAG, "onConfigurationChanged() " + newConfig);
116         super.onConfigurationChanged(newConfig);
117 
118         if (mCountDownLatch != null && mLatchEvent == EVENT_CONFIG) {
119             mCountDownLatch.countDown();
120         }
121     }
122 
getCurrentInputViewStarted()123     public boolean getCurrentInputViewStarted() {
124         return mInputViewStarted;
125     }
126 
127     /**
128      * Sets the latch used to wait for the IME event.
129      *
130      * @param latch      the latch to wait on.
131      * @param latchEvent the event to set the latch on.
132      */
setCountDownLatchForTesting(@ullable CountDownLatch latch, @Event int latchEvent)133     public void setCountDownLatchForTesting(@Nullable CountDownLatch latch, @Event int latchEvent) {
134         mCountDownLatch = latch;
135         mLatchEvent = latchEvent;
136     }
137 
138     /** Gets the last created instance of this wrapper, if available. */
139     @Nullable
getInstance()140     public static InputMethodServiceWrapper getInstance() {
141         return sInstance.get();
142     }
143 
144     /**
145      * Gets the string representation of the IME event that is being waited on.
146      *
147      * @param eventType the IME event type.
148      */
149     @NonNull
eventToString(@vent int eventType)150     public static String eventToString(@Event int eventType) {
151         return switch (eventType) {
152             case EVENT_SHOW -> "onStartInputView";
153             case EVENT_HIDE -> "onFinishInputView";
154             case EVENT_CONFIG -> "onConfigurationChanged";
155             default -> "unknownEvent";
156         };
157     }
158 
159     @NonNull
dumpEditorInfo(EditorInfo info)160     private String dumpEditorInfo(EditorInfo info) {
161         if (info == null) {
162             return "null";
163         }
164         return "EditorInfo{packageName=" + info.packageName
165                 + " fieldId=" + info.fieldId
166                 + " hintText=" + info.hintText
167                 + " privateImeOptions=" + info.privateImeOptions
168                 + "}";
169     }
170 }
171