182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/*
282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Copyright (C) 2017 The Android Open Source Project
382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Licensed under the Apache License, Version 2.0 (the "License");
582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * you may not use this file except in compliance with the License.
682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * You may obtain a copy of the License at
782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *      http://www.apache.org/licenses/LICENSE-2.0
982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
1082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Unless required by applicable law or agreed to in writing, software
1182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * distributed under the License is distributed on an "AS IS" BASIS,
1282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * See the License for the specific language governing permissions and
1482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * limitations under the License.
1582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */
1682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpackage android.support.text.emoji.util;
1782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
1868e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinirimport android.app.Instrumentation;
1968e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinirimport android.text.Selection;
2068e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinirimport android.text.Spannable;
2168e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinirimport android.text.method.QwertyKeyListener;
2268e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinirimport android.text.method.TextKeyListener;
2382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.view.KeyEvent;
2468e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinirimport android.view.inputmethod.EditorInfo;
25f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.view.inputmethod.InputConnection;
2668e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinirimport android.widget.TextView;
2768e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir
2868e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinirimport java.util.concurrent.CountDownLatch;
2982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
3082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/**
3182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Utility class for KeyEvents
3282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */
3382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpublic class KeyboardUtil {
3482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static final int ALT = KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON;
3582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static final int CTRL = KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON;
3682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static final int SHIFT = KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON;
3782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static final int FN = KeyEvent.META_FUNCTION_ON;
3882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
3982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static KeyEvent zero() {
4082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return keyEvent(KeyEvent.KEYCODE_0);
4182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
4282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
4382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static KeyEvent del() {
4482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return keyEvent(KeyEvent.KEYCODE_DEL);
4582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
4682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
4782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static KeyEvent altDel() {
4882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return keyEvent(KeyEvent.KEYCODE_DEL, ALT);
4982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
5082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static KeyEvent ctrlDel() {
5282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return keyEvent(KeyEvent.KEYCODE_DEL, CTRL);
5382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
5482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static KeyEvent shiftDel() {
5682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return keyEvent(KeyEvent.KEYCODE_DEL, SHIFT);
5782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
5882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static KeyEvent fnDel() {
6082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return keyEvent(KeyEvent.KEYCODE_DEL, FN);
6182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
6282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static KeyEvent forwardDel() {
6482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return keyEvent(KeyEvent.KEYCODE_FORWARD_DEL);
6582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
6682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static KeyEvent keyEvent(int keycode, int metaState) {
6882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final long currentTime = System.currentTimeMillis();
6982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN, keycode, 0, metaState);
7082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
7182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
7282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static KeyEvent keyEvent(int keycode) {
7382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final long currentTime = System.currentTimeMillis();
7482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN, keycode, 0);
7582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
76f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
7768e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir    public static void setComposingTextInBatch(final Instrumentation instrumentation,
7868e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            final InputConnection inputConnection, final CharSequence text)
7968e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            throws InterruptedException {
8068e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        final CountDownLatch latch = new CountDownLatch(1);
8168e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        instrumentation.runOnMainSync(new Runnable() {
8268e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            @Override
8368e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            public void run() {
8468e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                inputConnection.beginBatchEdit();
8568e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                inputConnection.setComposingText(text, 1);
8668e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                inputConnection.endBatchEdit();
8768e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                latch.countDown();
8868e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            }
8968e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        });
9068e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir
9168e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        latch.await();
9268e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        instrumentation.waitForIdleSync();
9368e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir    }
9468e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir
9568e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir    public static void deleteSurroundingText(final Instrumentation instrumentation,
9668e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            final InputConnection inputConnection, final int before, final int after)
9768e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            throws InterruptedException {
9868e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        final CountDownLatch latch = new CountDownLatch(1);
9968e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        instrumentation.runOnMainSync(new Runnable() {
10068e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            @Override
10168e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            public void run() {
10268e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                inputConnection.beginBatchEdit();
10368e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                inputConnection.deleteSurroundingText(before, after);
10468e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                inputConnection.endBatchEdit();
10568e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                latch.countDown();
10668e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            }
10768e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        });
10868e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        latch.await();
10968e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        instrumentation.waitForIdleSync();
11068e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir    }
11168e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir
11268e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir    public static void setSelection(Instrumentation instrumentation, final Spannable spannable,
11368e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            final int start) throws InterruptedException {
11468e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        setSelection(instrumentation, spannable, start, start);
11568e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir    }
11668e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir
11768e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir    public static void setSelection(Instrumentation instrumentation, final Spannable spannable,
11868e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            final int start, final int end) throws InterruptedException {
11968e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        final CountDownLatch latch = new CountDownLatch(1);
12068e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        instrumentation.runOnMainSync(new Runnable() {
12168e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            @Override
12268e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            public void run() {
12368e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                Selection.setSelection(spannable, start, end);
12468e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                latch.countDown();
12568e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            }
12668e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        });
12768e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        latch.await();
12868e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        instrumentation.waitForIdleSync();
129f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
130f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
13168e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir    public static InputConnection initTextViewForSimulatedIme(Instrumentation instrumentation,
13268e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            final TextView textView) throws InterruptedException {
13368e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        final CountDownLatch latch = new CountDownLatch(1);
13468e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        instrumentation.runOnMainSync(new Runnable() {
13568e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            @Override
13668e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            public void run() {
13768e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                textView.setKeyListener(
13868e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                        QwertyKeyListener.getInstance(false, TextKeyListener.Capitalize.NONE));
13968e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                textView.setText("", TextView.BufferType.EDITABLE);
14068e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir                latch.countDown();
14168e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir            }
14268e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        });
14368e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        latch.await();
14468e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        instrumentation.waitForIdleSync();
14568e7d24c66fb57ae9c2fa538a1cb0b9116673c25Siyamed Sinir        return textView.onCreateInputConnection(new EditorInfo());
146f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
14782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir}
148