1 /*
2  * Copyright (C) 2020 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.view.inputmethod.cts.util;
18 
19 import static org.junit.Assert.assertNotNull;
20 
21 import android.content.Context;
22 import android.support.test.uiautomator.By;
23 import android.support.test.uiautomator.BySelector;
24 import android.support.test.uiautomator.UiDevice;
25 import android.support.test.uiautomator.UiObject2;
26 import android.view.inputmethod.EditorInfo;
27 import android.view.inputmethod.InputConnection;
28 import android.webkit.WebView;
29 import android.webkit.WebViewClient;
30 import android.widget.EditText;
31 import android.widget.LinearLayout;
32 
33 import androidx.test.platform.app.InstrumentationRegistry;
34 
35 import com.android.compatibility.common.util.Timeout;
36 
37 import java.util.concurrent.CountDownLatch;
38 import java.util.concurrent.TimeUnit;
39 import java.util.concurrent.atomic.AtomicReference;
40 
41 /**
42  * A custom {@link WebView} class which provides a simple web page for verifying IME behavior.
43  */
44 public class TestWebView {
45 
46     private static final class Impl extends WebView {
47         private static final String MY_HTML =
48                 "<html><body>Editor: <input type='text' name='testInput'></body></html>";
49         private UiDevice mUiDevice;
50         private final String mMaker;
51 
Impl(Context context, UiDevice uiDevice, String maker)52         Impl(Context context, UiDevice uiDevice, String maker) {
53             super(context);
54             mUiDevice = uiDevice;
55             mMaker = maker;
56         }
57 
58         @Override
onCreateInputConnection(EditorInfo outAttrs)59         public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
60             final InputConnection original = super.onCreateInputConnection(outAttrs);
61             final int inputType = outAttrs.inputType;
62             if ((inputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT
63                     && (inputType & EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) != 0) {
64                 outAttrs.privateImeOptions = mMaker;
65             }
66             return original;
67         }
68 
loadEditorPage()69         private void loadEditorPage() {
70             super.loadData(MY_HTML, "text/html", "UTF-8");
71         }
72 
getInput(Timeout timeout)73         private UiObject2 getInput(Timeout timeout) {
74             final BySelector selector = By.text("Editor: ");
75             UiObject2 label = null;
76             try {
77                 label = timeout.run("waitForObject(" + selector + ")",
78                         () -> mUiDevice.findObject(selector));
79             } catch (Exception e) {
80             }
81             assertNotNull("Editor label must exists on WebView", label);
82 
83             // Then the input is next.
84             final UiObject2 parent = label.getParent();
85             UiObject2 previous = null;
86             for (UiObject2 child : parent.getChildren()) {
87                 if (label.equals(previous)) {
88                     if (child.getClassName().equals(EditText.class.getName())) {
89                         return child;
90                     }
91                 }
92                 previous = child;
93             }
94             return null;
95         }
96     }
97 
98     /**
99      * Not intended to be instantiated.
100      */
TestWebView()101     private TestWebView() {
102     }
103 
launchTestWebViewActivity(long timeoutMs, String maker)104     public static UiObject2 launchTestWebViewActivity(long timeoutMs, String maker)
105             throws Exception {
106         final AtomicReference<UiObject2> inputTextFieldRef = new AtomicReference<>();
107         final AtomicReference<TestWebView.Impl> webViewRef = new AtomicReference<>();
108         final CountDownLatch latch = new CountDownLatch(1);
109         final Timeout timeoutForFindObj = new Timeout("UIOBJECT_FIND_TIMEOUT",
110                 timeoutMs, 2F, timeoutMs);
111 
112         TestActivity.startSync(activity -> {
113             final LinearLayout layout = new LinearLayout(activity);
114             final TestWebView.Impl webView = new Impl(activity, UiDevice.getInstance(
115                     InstrumentationRegistry.getInstrumentation()), maker);
116             webView.setWebViewClient(new WebViewClient() {
117                 @Override
118                 public void onPageFinished(WebView view, String url) {
119                     latch.countDown();
120                 }
121             });
122             webViewRef.set(webView);
123             layout.setOrientation(LinearLayout.VERTICAL);
124             layout.addView(webView);
125             webView.loadEditorPage();
126             return layout;
127         });
128 
129         latch.await(timeoutMs, TimeUnit.MILLISECONDS);
130         inputTextFieldRef.set(webViewRef.get().getInput(timeoutForFindObj));
131         return inputTextFieldRef.get();
132     }
133 }
134