18a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk/* 28a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Copyright 2017 The Android Open Source Project 38a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * 48a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Licensed under the Apache License, Version 2.0 (the "License"); 58a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * you may not use this file except in compliance with the License. 68a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * You may obtain a copy of the License at 78a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * 88a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * http://www.apache.org/licenses/LICENSE-2.0 98a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * 108a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Unless required by applicable law or agreed to in writing, software 118a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * distributed under the License is distributed on an "AS IS" BASIS, 128a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * See the License for the specific language governing permissions and 148a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * limitations under the License. 158a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 168a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1785ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikaspackage androidx.slice.widget; 188a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 198a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.animation.Animator; 208a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.app.PendingIntent; 218a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.app.RemoteInput; 228a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.content.Context; 238a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.content.Intent; 248a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.graphics.Rect; 258a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.graphics.drawable.Drawable; 26f68dde011de8fb4d616a09292524869f56d94110Jason Monkimport android.os.Build; 278a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.os.Bundle; 288a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.text.Editable; 298a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.text.TextWatcher; 308a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.util.AttributeSet; 318a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.util.Log; 328a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.KeyEvent; 338a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.LayoutInflater; 348a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.MotionEvent; 358a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.View; 368a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.ViewAnimationUtils; 378a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.ViewGroup; 388a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.accessibility.AccessibilityEvent; 398a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.inputmethod.CompletionInfo; 408a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.inputmethod.EditorInfo; 418a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.inputmethod.InputConnection; 428a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.inputmethod.InputMethodManager; 438a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.widget.EditText; 448a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.widget.ImageButton; 458a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.widget.LinearLayout; 468a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.widget.ProgressBar; 478a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.widget.TextView; 488a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.widget.Toast; 498a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 50f68dde011de8fb4d616a09292524869f56d94110Jason Monkimport androidx.annotation.RequiresApi; 51f68dde011de8fb4d616a09292524869f56d94110Jason Monkimport androidx.annotation.RestrictTo; 52f68dde011de8fb4d616a09292524869f56d94110Jason Monkimport androidx.core.content.ContextCompat; 5307a4a56611cc044fd48b052db05aea332201216eJason Monkimport androidx.slice.SliceItem; 5485ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.view.R; 558a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 568a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk/** 578a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Host for the remote input. 588a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * 598a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * @hide 608a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 618a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk// TODO this should be unified with SystemUI RemoteInputView (b/67527720) 628a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk@RestrictTo(RestrictTo.Scope.LIBRARY) 63f68dde011de8fb4d616a09292524869f56d94110Jason Monk@RequiresApi(21) 648a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkpublic class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher { 658a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 668a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private static final String TAG = "RemoteInput"; 678a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 688a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk /** 698a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * A marker object that let's us easily find views of this class. 708a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 718a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public static final Object VIEW_TAG = new Object(); 728a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 738a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private RemoteEditText mEditText; 748a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private ImageButton mSendButton; 758a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private ProgressBar mProgressBar; 7607a4a56611cc044fd48b052db05aea332201216eJason Monk private SliceItem mAction; 778a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private RemoteInput[] mRemoteInputs; 788a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private RemoteInput mRemoteInput; 798a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 808a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private int mRevealCx; 818a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private int mRevealCy; 828a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private int mRevealR; 838a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private boolean mResetting; 848a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 858a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public RemoteInputView(Context context, AttributeSet attrs) { 868a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk super(context, attrs); 878a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 888a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 898a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 908a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk protected void onFinishInflate() { 918a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk super.onFinishInflate(); 928a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 938a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mProgressBar = findViewById(R.id.remote_input_progress); 948a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mSendButton = findViewById(R.id.remote_input_send); 958a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mSendButton.setOnClickListener(this); 968a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 978a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText = (RemoteEditText) getChildAt(0); 988a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { 998a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 1008a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 1018a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk final boolean isSoftImeEvent = event == null 1028a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk && (actionId == EditorInfo.IME_ACTION_DONE 1038a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk || actionId == EditorInfo.IME_ACTION_NEXT 1048a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk || actionId == EditorInfo.IME_ACTION_SEND); 1058a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk final boolean isKeyboardEnterKey = event != null 1068a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk && isConfirmKey(event.getKeyCode()) 1078a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk && event.getAction() == KeyEvent.ACTION_DOWN; 1088a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1098a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (isSoftImeEvent || isKeyboardEnterKey) { 1108a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (mEditText.length() > 0) { 1118a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk sendRemoteInput(); 1128a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1138a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // Consume action to prevent IME from closing. 1148a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return true; 1158a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1168a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return false; 1178a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1188a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk }); 1198a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.addTextChangedListener(this); 1208a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.setInnerFocusable(false); 1218a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.mRemoteInputView = this; 1228a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1238a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1248a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private void sendRemoteInput() { 1258a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk Bundle results = new Bundle(); 1268a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString()); 1278a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 1288a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent, 1298a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk results); 1308a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1318a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.setEnabled(false); 1328a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mSendButton.setVisibility(INVISIBLE); 1338a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mProgressBar.setVisibility(VISIBLE); 1348a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.mShowImeOnInputConnection = false; 1358a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1368a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // TODO: Figure out API for telling the system about slice interaction. 1378a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // Tell ShortcutManager that this package has been "activated". ShortcutManager 1388a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // will reset the throttling for this package. 1398a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // Strictly speaking, the intent receiver may be different from the intent creator, 1408a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // but that's an edge case, and also because we can't always know which package will receive 1418a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // an intent, so we just reset for the creator. 1428a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk //getContext().getSystemService(ShortcutManager.class).onApplicationActive( 14307a4a56611cc044fd48b052db05aea332201216eJason Monk // mAction.getCreatorPackage(), 1448a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // getContext().getUserId()); 1458a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1468a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk try { 14707a4a56611cc044fd48b052db05aea332201216eJason Monk mAction.fireAction(getContext(), fillInIntent); 1488a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk reset(); 1498a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } catch (PendingIntent.CanceledException e) { 1508a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk Log.i(TAG, "Unable to send remote input result", e); 1518a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk Toast.makeText(getContext(), "Failure sending pending intent for inline reply :(", 1528a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk Toast.LENGTH_SHORT).show(); 1538a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk reset(); 1548a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1558a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1568a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1578a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk /** 1588a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Creates a remote input view. 1598a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 1608a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public static RemoteInputView inflate(Context context, ViewGroup root) { 1618a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate( 1628a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk R.layout.abc_slice_remote_input, root, false); 1638a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk v.setTag(VIEW_TAG); 1648a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return v; 1658a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1668a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1678a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 1688a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void onClick(View v) { 1698a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (v == mSendButton) { 1708a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk sendRemoteInput(); 1718a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1728a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1738a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1748a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 1758a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public boolean onTouchEvent(MotionEvent event) { 1768a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk super.onTouchEvent(event); 1778a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1788a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // We never want for a touch to escape to an outer view or one we covered. 1798a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return true; 1808a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1818a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1828a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private void onDefocus() { 1838a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setVisibility(INVISIBLE); 1848a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1858a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1868a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk /** 1878a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Set the pending intent for remote input. 1888a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 18907a4a56611cc044fd48b052db05aea332201216eJason Monk public void setAction(SliceItem action) { 19007a4a56611cc044fd48b052db05aea332201216eJason Monk mAction = action; 1918a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 1928a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 1938a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk /** 1948a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Set the remote inputs for this view. 1958a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 1968a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) { 1978a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mRemoteInputs = remoteInputs; 1988a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mRemoteInput = remoteInput; 1998a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.setHint(mRemoteInput.getLabel()); 2008a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2018a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2028a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk /** 2038a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Focuses the remote input view. 2048a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 2058a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void focusAnimated() { 2068a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (getVisibility() != VISIBLE) { 2078a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk Animator animator = ViewAnimationUtils.createCircularReveal( 2088a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk this, mRevealCx, mRevealCy, 0, mRevealR); 2098a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk animator.setDuration(200); 2108a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk animator.start(); 2118a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2128a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk focus(); 2138a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2148a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2158a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private void focus() { 2168a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setVisibility(VISIBLE); 2178a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.setInnerFocusable(true); 2188a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.mShowImeOnInputConnection = true; 2198a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.setSelection(mEditText.getText().length()); 2208a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.requestFocus(); 2218a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk updateSendButton(); 2228a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2238a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2248a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private void reset() { 2258a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mResetting = true; 2268a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2278a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.getText().clear(); 2288a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mEditText.setEnabled(true); 2298a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mSendButton.setVisibility(VISIBLE); 2308a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mProgressBar.setVisibility(INVISIBLE); 2318a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk updateSendButton(); 2328a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk onDefocus(); 2338a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2348a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mResetting = false; 2358a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2368a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2378a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 2388a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { 2398a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (mResetting && child == mEditText) { 2408a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // Suppress text events if it happens during resetting. Ideally this would be 2418a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // suppressed by the text view not being shown, but that doesn't work here because it 2428a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // needs to stay visible for the animation. 2438a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return false; 2448a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2458a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return super.onRequestSendAccessibilityEvent(child, event); 2468a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2478a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2488a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private void updateSendButton() { 2498a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mSendButton.setEnabled(mEditText.getText().length() != 0); 2508a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2518a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2528a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 2538a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void beforeTextChanged(CharSequence s, int start, int count, int after) { 2548a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2558a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2568a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 2578a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void onTextChanged(CharSequence s, int start, int before, int count) { 2588a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2598a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2608a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 2618a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void afterTextChanged(Editable s) { 2628a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk updateSendButton(); 2638a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2648a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2658a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk /** 2668a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * @hide 2678a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 2688a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @RestrictTo(RestrictTo.Scope.LIBRARY) 2698a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void setRevealParameters(int cx, int cy, int r) { 2708a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mRevealCx = cx; 2718a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mRevealCy = cy; 2728a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mRevealR = r; 2738a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2748a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2758a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 2768a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void dispatchStartTemporaryDetach() { 2778a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk super.dispatchStartTemporaryDetach(); 2788a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and 2798a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // won't lose IME focus. 2808a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk detachViewFromParent(mEditText); 2818a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2828a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2838a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 2848a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void dispatchFinishTemporaryDetach() { 2858a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (isAttachedToWindow()) { 2868a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk attachViewToParent(mEditText, 0, mEditText.getLayoutParams()); 2878a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } else { 2888a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk removeDetachedView(mEditText, false /* animate */); 2898a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2908a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk super.dispatchFinishTemporaryDetach(); 2918a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 2928a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2938a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk /** 2948a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * An EditText that changes appearance based on whether it's focusable and becomes un-focusable 2958a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * whenever the user navigates away from it or it becomes invisible. 2968a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 2978a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public static class RemoteEditText extends EditText { 2988a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 2998a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private final Drawable mBackground; 3008a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private RemoteInputView mRemoteInputView; 3018a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk boolean mShowImeOnInputConnection; 3028a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3038a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public RemoteEditText(Context context, AttributeSet attrs) { 3048a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk super(context, attrs); 3058a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mBackground = getBackground(); 3068a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3078a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3088a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk private void defocusIfNeeded(boolean animate) { 309f68dde011de8fb4d616a09292524869f56d94110Jason Monk if (mRemoteInputView != null || isTemporarilyDetachedCompat()) { 310f68dde011de8fb4d616a09292524869f56d94110Jason Monk if (isTemporarilyDetachedCompat()) { 3118a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // We might get reattached but then the other one of HUN / expanded might steal 3128a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // our focus, so we'll need to save our text here. 3138a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3148a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return; 3158a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3168a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (isFocusable() && isEnabled()) { 3178a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setInnerFocusable(false); 3188a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (mRemoteInputView != null) { 3198a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mRemoteInputView.onDefocus(); 3208a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3218a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk mShowImeOnInputConnection = false; 3228a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3238a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3248a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 325f68dde011de8fb4d616a09292524869f56d94110Jason Monk private boolean isTemporarilyDetachedCompat() { 326f68dde011de8fb4d616a09292524869f56d94110Jason Monk if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 327f68dde011de8fb4d616a09292524869f56d94110Jason Monk return isTemporarilyDetached(); 328f68dde011de8fb4d616a09292524869f56d94110Jason Monk } 329f68dde011de8fb4d616a09292524869f56d94110Jason Monk return false; 330f68dde011de8fb4d616a09292524869f56d94110Jason Monk } 331f68dde011de8fb4d616a09292524869f56d94110Jason Monk 3328a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 3338a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk protected void onVisibilityChanged(View changedView, int visibility) { 3348a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk super.onVisibilityChanged(changedView, visibility); 3358a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3368a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (!isShown()) { 3378a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk defocusIfNeeded(false /* animate */); 3388a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3398a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3408a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3418a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 3428a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 3438a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk super.onFocusChanged(focused, direction, previouslyFocusedRect); 3448a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (!focused) { 3458a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk defocusIfNeeded(true /* animate */); 3468a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3478a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3488a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3498a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 3508a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void getFocusedRect(Rect r) { 3518a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk super.getFocusedRect(r); 3528a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk r.top = getScrollY(); 3538a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk r.bottom = getScrollY() + (getBottom() - getTop()); 3548a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3558a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3568a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 3578a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public boolean onKeyDown(int keyCode, KeyEvent event) { 3588a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (keyCode == KeyEvent.KEYCODE_BACK) { 3598a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // Eat the DOWN event here to prevent any default behavior. 3608a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return true; 3618a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3628a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return super.onKeyDown(keyCode, event); 3638a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3648a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3658a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 3668a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public boolean onKeyUp(int keyCode, KeyEvent event) { 3678a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (keyCode == KeyEvent.KEYCODE_BACK) { 3688a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk defocusIfNeeded(true /* animate */); 3698a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return true; 3708a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3718a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return super.onKeyUp(keyCode, event); 3728a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3738a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3748a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 3758a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 3768a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk final InputConnection inputConnection = super.onCreateInputConnection(outAttrs); 3778a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3788a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (mShowImeOnInputConnection && inputConnection != null) { 37906ab4aa38f82b8e2d9af769f7c777253f2497f93Jake Wharton final InputMethodManager imm = ContextCompat.getSystemService(getContext(), 3808a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk InputMethodManager.class); 3818a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (imm != null) { 3828a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // onCreateInputConnection is called by InputMethodManager in the middle of 3838a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // setting up the connection to the IME; wait with requesting the IME until that 3848a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk // work has completed. 3858a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk post(new Runnable() { 3868a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 3878a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void run() { 3888a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk imm.viewClicked(RemoteEditText.this); 3898a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk imm.showSoftInput(RemoteEditText.this, 0); 3908a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3918a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk }); 3928a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3938a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3948a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3958a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return inputConnection; 3968a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 3978a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 3988a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @Override 3998a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public void onCommitCompletion(CompletionInfo text) { 4008a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk clearComposingText(); 4018a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setText(text.getText()); 4028a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setSelection(getText().length()); 4038a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 4048a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 4058a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk void setInnerFocusable(boolean focusable) { 4068a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setFocusableInTouchMode(focusable); 4078a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setFocusable(focusable); 4088a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setCursorVisible(focusable); 4098a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 4108a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk if (focusable) { 4118a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk requestFocus(); 4128a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setBackground(mBackground); 4138a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } else { 4148a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk setBackground(null); 4158a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 4168a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 4178a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 4188a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 4198a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk 4208a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk /** Whether key will, by default, trigger a click on the focused view. 4218a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * @hide 4228a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */ 4238a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk @RestrictTo(RestrictTo.Scope.LIBRARY) 4248a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk public static final boolean isConfirmKey(int keyCode) { 4258a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk switch (keyCode) { 4268a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk case KeyEvent.KEYCODE_DPAD_CENTER: 4278a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk case KeyEvent.KEYCODE_ENTER: 4288a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk case KeyEvent.KEYCODE_SPACE: 4298a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk case KeyEvent.KEYCODE_NUMPAD_ENTER: 4308a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return true; 4318a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk default: 4328a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk return false; 4338a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 4348a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk } 4358a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk} 436