blob: b63c1da59cba77fae39855bac7b854459533ff3e [file] [log] [blame]
Adrian Roos497ab022015-02-10 20:49:33 +01001/*
2 * Copyright (C) 2015 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
17package com.android.systemui.statusbar.policy;
18
Adrian Roosaf06bf22016-07-15 12:26:49 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Adrian Roos4c1fcc82016-03-31 14:39:39 -070021import android.app.Notification;
Adrian Roos497ab022015-02-10 20:49:33 +010022import android.app.PendingIntent;
23import android.app.RemoteInput;
24import android.content.Context;
25import android.content.Intent;
Makoto Onukid6e1f3b2016-06-14 11:17:59 -070026import android.content.pm.ShortcutManager;
Adrian Roosdc5b4532016-01-06 20:49:41 +010027import android.graphics.Rect;
Adrian Roos497ab022015-02-10 20:49:33 +010028import android.graphics.drawable.Drawable;
29import android.os.Bundle;
Selim Cinek1397ea32018-01-16 17:34:52 -080030import android.os.SystemClock;
Adrian Roos245aa872015-12-07 14:53:53 -080031import android.text.Editable;
Selim Cinek1397ea32018-01-16 17:34:52 -080032import android.text.SpannedString;
Adrian Roos245aa872015-12-07 14:53:53 -080033import android.text.TextWatcher;
Adrian Roos497ab022015-02-10 20:49:33 +010034import android.util.AttributeSet;
35import android.util.Log;
36import android.view.KeyEvent;
37import android.view.LayoutInflater;
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070038import android.view.MotionEvent;
Adrian Roos497ab022015-02-10 20:49:33 +010039import android.view.View;
Adrian Roosaf06bf22016-07-15 12:26:49 -070040import android.view.ViewAnimationUtils;
Adrian Roos497ab022015-02-10 20:49:33 +010041import android.view.ViewGroup;
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070042import android.view.ViewParent;
Adrian Roosf17c86b2016-09-07 14:08:40 -070043import android.view.accessibility.AccessibilityEvent;
Adrian Roosfe84e1f2015-11-04 15:55:39 -080044import android.view.inputmethod.CompletionInfo;
Adrian Roos497ab022015-02-10 20:49:33 +010045import android.view.inputmethod.EditorInfo;
Adrian Roos1c0ca502015-10-07 12:20:42 -070046import android.view.inputmethod.InputConnection;
Adrian Roos497ab022015-02-10 20:49:33 +010047import android.view.inputmethod.InputMethodManager;
48import android.widget.EditText;
Adrian Roosfe84e1f2015-11-04 15:55:39 -080049import android.widget.ImageButton;
50import android.widget.LinearLayout;
Adrian Roos497ab022015-02-10 20:49:33 +010051import android.widget.ProgressBar;
52import android.widget.TextView;
53
Adrian Roosceeb04c2016-04-25 14:00:54 -070054import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010055import com.android.internal.logging.nano.MetricsProto;
Adrian Roosaf06bf22016-07-15 12:26:49 -070056import com.android.systemui.Interpolators;
Winsonc0d70582016-01-29 10:24:39 -080057import com.android.systemui.R;
58import com.android.systemui.statusbar.NotificationData;
59import com.android.systemui.statusbar.RemoteInputController;
Adrian Roos7b9ed0d2017-01-24 15:55:18 -080060import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Adrian Roosaf06bf22016-07-15 12:26:49 -070061import com.android.systemui.statusbar.stack.StackStateAnimator;
Adrian Roos1c0ca502015-10-07 12:20:42 -070062
Adrian Roos497ab022015-02-10 20:49:33 +010063/**
64 * Host for the remote input.
65 */
Adrian Roos245aa872015-12-07 14:53:53 -080066public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
Adrian Roos497ab022015-02-10 20:49:33 +010067
68 private static final String TAG = "RemoteInput";
69
Adrian Roosfe84e1f2015-11-04 15:55:39 -080070 // A marker object that let's us easily find views of this class.
71 public static final Object VIEW_TAG = new Object();
72
Adrian Roos7813dd72016-09-23 17:12:17 -070073 public final Object mToken = new Object();
74
Adrian Roos497ab022015-02-10 20:49:33 +010075 private RemoteEditText mEditText;
Adrian Roosfe84e1f2015-11-04 15:55:39 -080076 private ImageButton mSendButton;
Adrian Roos497ab022015-02-10 20:49:33 +010077 private ProgressBar mProgressBar;
78 private PendingIntent mPendingIntent;
Adrian Roosfe84e1f2015-11-04 15:55:39 -080079 private RemoteInput[] mRemoteInputs;
Adrian Roos497ab022015-02-10 20:49:33 +010080 private RemoteInput mRemoteInput;
Adrian Roos1c0ca502015-10-07 12:20:42 -070081 private RemoteInputController mController;
Adrian Roosfe84e1f2015-11-04 15:55:39 -080082
Adrian Roos1c0ca502015-10-07 12:20:42 -070083 private NotificationData.Entry mEntry;
Adrian Roos5153d4a2016-03-22 10:01:56 -070084
Adrian Roosd009ab12016-05-20 17:58:53 -070085 private boolean mRemoved;
Adrian Roos497ab022015-02-10 20:49:33 +010086
Adrian Roosaf06bf22016-07-15 12:26:49 -070087 private int mRevealCx;
88 private int mRevealCy;
89 private int mRevealR;
90
Adrian Roosf17c86b2016-09-07 14:08:40 -070091 private boolean mResetting;
Adrian Roos7b9ed0d2017-01-24 15:55:18 -080092 private NotificationViewWrapper mWrapper;
Adrian Roosf17c86b2016-09-07 14:08:40 -070093
Adrian Roos497ab022015-02-10 20:49:33 +010094 public RemoteInputView(Context context, AttributeSet attrs) {
95 super(context, attrs);
96 }
97
98 @Override
99 protected void onFinishInflate() {
100 super.onFinishInflate();
101
Alan Viverette51efddb2017-04-05 10:00:01 -0400102 mProgressBar = findViewById(R.id.remote_input_progress);
Adrian Roos497ab022015-02-10 20:49:33 +0100103
Alan Viverette51efddb2017-04-05 10:00:01 -0400104 mSendButton = findViewById(R.id.remote_input_send);
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800105 mSendButton.setOnClickListener(this);
106
Adrian Roos497ab022015-02-10 20:49:33 +0100107 mEditText = (RemoteEditText) getChildAt(0);
108 mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
109 @Override
110 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Adrian Roos497ab022015-02-10 20:49:33 +0100111 final boolean isSoftImeEvent = event == null
112 && (actionId == EditorInfo.IME_ACTION_DONE
113 || actionId == EditorInfo.IME_ACTION_NEXT
114 || actionId == EditorInfo.IME_ACTION_SEND);
115 final boolean isKeyboardEnterKey = event != null
116 && KeyEvent.isConfirmKey(event.getKeyCode())
117 && event.getAction() == KeyEvent.ACTION_DOWN;
118
119 if (isSoftImeEvent || isKeyboardEnterKey) {
Adrian Roos6db43cb2016-04-29 10:45:38 -0700120 if (mEditText.length() > 0) {
121 sendRemoteInput();
122 }
123 // Consume action to prevent IME from closing.
Adrian Roos497ab022015-02-10 20:49:33 +0100124 return true;
125 }
126 return false;
127 }
128 });
Adrian Roos245aa872015-12-07 14:53:53 -0800129 mEditText.addTextChangedListener(this);
Adrian Roos497ab022015-02-10 20:49:33 +0100130 mEditText.setInnerFocusable(false);
Adrian Roos5153d4a2016-03-22 10:01:56 -0700131 mEditText.mRemoteInputView = this;
Adrian Roos497ab022015-02-10 20:49:33 +0100132 }
133
134 private void sendRemoteInput() {
135 Bundle results = new Bundle();
136 results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
Adrian Roos1c0ca502015-10-07 12:20:42 -0700137 Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800138 RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
Adrian Roos497ab022015-02-10 20:49:33 +0100139 results);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000140 RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT);
Adrian Roos497ab022015-02-10 20:49:33 +0100141
142 mEditText.setEnabled(false);
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800143 mSendButton.setVisibility(INVISIBLE);
Adrian Roos497ab022015-02-10 20:49:33 +0100144 mProgressBar.setVisibility(VISIBLE);
Adrian Roosc0a579e2016-03-30 16:43:58 -0700145 mEntry.remoteInputText = mEditText.getText();
Selim Cinek1397ea32018-01-16 17:34:52 -0800146 mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime();
Adrian Roos7813dd72016-09-23 17:12:17 -0700147 mController.addSpinning(mEntry.key, mToken);
148 mController.removeRemoteInput(mEntry, mToken);
Adrian Roos245aa872015-12-07 14:53:53 -0800149 mEditText.mShowImeOnInputConnection = false;
Adrian Roosc0a579e2016-03-30 16:43:58 -0700150 mController.remoteInputSent(mEntry);
Adrian Roos497ab022015-02-10 20:49:33 +0100151
Makoto Onukid6e1f3b2016-06-14 11:17:59 -0700152 // Tell ShortcutManager that this package has been "activated". ShortcutManager
153 // will reset the throttling for this package.
154 // Strictly speaking, the intent receiver may be different from the notification publisher,
155 // but that's an edge case, and also because we can't always know which package will receive
156 // an intent, so we just reset for the publisher.
157 getContext().getSystemService(ShortcutManager.class).onApplicationActive(
158 mEntry.notification.getPackageName(),
159 mEntry.notification.getUser().getIdentifier());
160
Adrian Roosceeb04c2016-04-25 14:00:54 -0700161 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
162 mEntry.notification.getPackageName());
Adrian Roos497ab022015-02-10 20:49:33 +0100163 try {
164 mPendingIntent.send(mContext, 0, fillInIntent);
165 } catch (PendingIntent.CanceledException e) {
166 Log.i(TAG, "Unable to send remote input result", e);
Adrian Roosceeb04c2016-04-25 14:00:54 -0700167 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
168 mEntry.notification.getPackageName());
Adrian Roos497ab022015-02-10 20:49:33 +0100169 }
170 }
171
172 public static RemoteInputView inflate(Context context, ViewGroup root,
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800173 NotificationData.Entry entry,
Adrian Roos1c0ca502015-10-07 12:20:42 -0700174 RemoteInputController controller) {
Adrian Roos497ab022015-02-10 20:49:33 +0100175 RemoteInputView v = (RemoteInputView)
176 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
Adrian Roos1c0ca502015-10-07 12:20:42 -0700177 v.mController = controller;
178 v.mEntry = entry;
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800179 v.setTag(VIEW_TAG);
Adrian Roos497ab022015-02-10 20:49:33 +0100180
181 return v;
182 }
183
184 @Override
185 public void onClick(View v) {
Adrian Roos5389a172016-04-20 13:58:03 -0700186 if (v == mSendButton) {
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800187 sendRemoteInput();
Adrian Roos497ab022015-02-10 20:49:33 +0100188 }
189 }
190
Adrian Roos5389a172016-04-20 13:58:03 -0700191 @Override
192 public boolean onTouchEvent(MotionEvent event) {
193 super.onTouchEvent(event);
194
195 // We never want for a touch to escape to an outer view or one we covered.
196 return true;
197 }
198
Adrian Roosaf06bf22016-07-15 12:26:49 -0700199 private void onDefocus(boolean animate) {
Adrian Roos7813dd72016-09-23 17:12:17 -0700200 mController.removeRemoteInput(mEntry, mToken);
Adrian Roos777ef562015-12-01 17:37:14 -0800201 mEntry.remoteInputText = mEditText.getText();
Adrian Roosd009ab12016-05-20 17:58:53 -0700202
203 // During removal, we get reattached and lose focus. Not hiding in that
204 // case to prevent flicker.
205 if (!mRemoved) {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700206 if (animate && mRevealR > 0) {
207 Animator reveal = ViewAnimationUtils.createCircularReveal(
208 this, mRevealCx, mRevealCy, mRevealR, 0);
209 reveal.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
210 reveal.setDuration(StackStateAnimator.ANIMATION_DURATION_CLOSE_REMOTE_INPUT);
211 reveal.addListener(new AnimatorListenerAdapter() {
212 @Override
213 public void onAnimationEnd(Animator animation) {
214 setVisibility(INVISIBLE);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800215 if (mWrapper != null) {
216 mWrapper.setRemoteInputVisible(false);
217 }
Adrian Roosaf06bf22016-07-15 12:26:49 -0700218 }
219 });
220 reveal.start();
221 } else {
222 setVisibility(INVISIBLE);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800223 if (mWrapper != null) {
224 mWrapper.setRemoteInputVisible(false);
225 }
Adrian Roosaf06bf22016-07-15 12:26:49 -0700226 }
Adrian Roosd009ab12016-05-20 17:58:53 -0700227 }
Adrian Roosceeb04c2016-04-25 14:00:54 -0700228 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
229 mEntry.notification.getPackageName());
Adrian Roos1c0ca502015-10-07 12:20:42 -0700230 }
231
232 @Override
Adrian Roos14503e22016-03-09 14:01:24 -0800233 protected void onAttachedToWindow() {
234 super.onAttachedToWindow();
235 if (mEntry.row.isChangingPosition()) {
236 if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
237 mEditText.requestFocus();
238 }
239 }
240 }
241
242 @Override
Adrian Roos1c0ca502015-10-07 12:20:42 -0700243 protected void onDetachedFromWindow() {
244 super.onDetachedFromWindow();
Adrian Roos7813dd72016-09-23 17:12:17 -0700245 if (mEntry.row.isChangingPosition() || isTemporarilyDetached()) {
Adrian Roos14503e22016-03-09 14:01:24 -0800246 return;
247 }
Adrian Roos7813dd72016-09-23 17:12:17 -0700248 mController.removeRemoteInput(mEntry, mToken);
249 mController.removeSpinning(mEntry.key, mToken);
Adrian Roos1c0ca502015-10-07 12:20:42 -0700250 }
251
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800252 public void setPendingIntent(PendingIntent pendingIntent) {
253 mPendingIntent = pendingIntent;
254 }
255
256 public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
257 mRemoteInputs = remoteInputs;
258 mRemoteInput = remoteInput;
259 mEditText.setHint(mRemoteInput.getLabel());
260 }
261
Adrian Roosaf06bf22016-07-15 12:26:49 -0700262 public void focusAnimated() {
263 if (getVisibility() != VISIBLE) {
264 Animator animator = ViewAnimationUtils.createCircularReveal(
265 this, mRevealCx, mRevealCy, 0, mRevealR);
266 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
267 animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
268 animator.start();
269 }
270 focus();
271 }
272
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800273 public void focus() {
Adrian Roosceeb04c2016-04-25 14:00:54 -0700274 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
275 mEntry.notification.getPackageName());
276
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700277 setVisibility(VISIBLE);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800278 if (mWrapper != null) {
279 mWrapper.setRemoteInputVisible(true);
280 }
Adrian Roos7813dd72016-09-23 17:12:17 -0700281 mController.addRemoteInput(mEntry, mToken);
Adrian Roosdc5b4532016-01-06 20:49:41 +0100282 mEditText.setInnerFocusable(true);
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800283 mEditText.mShowImeOnInputConnection = true;
Adrian Roos777ef562015-12-01 17:37:14 -0800284 mEditText.setText(mEntry.remoteInputText);
285 mEditText.setSelection(mEditText.getText().length());
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800286 mEditText.requestFocus();
Adrian Roos245aa872015-12-07 14:53:53 -0800287 updateSendButton();
288 }
289
Adrian Roos0789ee02016-06-01 11:34:58 -0700290 public void onNotificationUpdateOrReset() {
Adrian Roos245aa872015-12-07 14:53:53 -0800291 boolean sending = mProgressBar.getVisibility() == VISIBLE;
292
293 if (sending) {
294 // Update came in after we sent the reply, time to reset.
295 reset();
296 }
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800297
298 if (isActive() && mWrapper != null) {
299 mWrapper.setRemoteInputVisible(true);
300 }
Adrian Roos245aa872015-12-07 14:53:53 -0800301 }
302
303 private void reset() {
Adrian Roosf17c86b2016-09-07 14:08:40 -0700304 mResetting = true;
Selim Cinek1397ea32018-01-16 17:34:52 -0800305 mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText());
Adrian Roosf17c86b2016-09-07 14:08:40 -0700306
Adrian Roos245aa872015-12-07 14:53:53 -0800307 mEditText.getText().clear();
308 mEditText.setEnabled(true);
309 mSendButton.setVisibility(VISIBLE);
310 mProgressBar.setVisibility(INVISIBLE);
Adrian Roos7813dd72016-09-23 17:12:17 -0700311 mController.removeSpinning(mEntry.key, mToken);
Adrian Roos245aa872015-12-07 14:53:53 -0800312 updateSendButton();
Adrian Roosaf06bf22016-07-15 12:26:49 -0700313 onDefocus(false /* animate */);
Adrian Roosf17c86b2016-09-07 14:08:40 -0700314
315 mResetting = false;
316 }
317
318 @Override
319 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
320 if (mResetting && child == mEditText) {
321 // Suppress text events if it happens during resetting. Ideally this would be
322 // suppressed by the text view not being shown, but that doesn't work here because it
323 // needs to stay visible for the animation.
324 return false;
325 }
326 return super.onRequestSendAccessibilityEvent(child, event);
Adrian Roos245aa872015-12-07 14:53:53 -0800327 }
328
329 private void updateSendButton() {
330 mSendButton.setEnabled(mEditText.getText().length() != 0);
331 }
332
333 @Override
334 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
335
336 @Override
337 public void onTextChanged(CharSequence s, int start, int before, int count) {}
338
339 @Override
340 public void afterTextChanged(Editable s) {
341 updateSendButton();
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800342 }
343
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700344 public void close() {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700345 mEditText.defocusIfNeeded(false /* animated */);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700346 }
347
348 @Override
349 public boolean onInterceptTouchEvent(MotionEvent ev) {
350 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
yoshiki iguchiff4696c2017-11-22 16:47:41 +0900351 mController.requestDisallowLongPressAndDismiss();
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700352 }
353 return super.onInterceptTouchEvent(ev);
354 }
355
Adrian Roos5153d4a2016-03-22 10:01:56 -0700356 public boolean requestScrollTo() {
yoshiki iguchiff4696c2017-11-22 16:47:41 +0900357 mController.lockScrollTo(mEntry);
Adrian Roos5153d4a2016-03-22 10:01:56 -0700358 return true;
359 }
360
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700361 public boolean isActive() {
Adrian Roos9550f2e2016-08-23 18:23:01 +0200362 return mEditText.isFocused() && mEditText.isEnabled();
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700363 }
364
365 public void stealFocusFrom(RemoteInputView other) {
366 other.close();
367 setPendingIntent(other.mPendingIntent);
368 setRemoteInput(other.mRemoteInputs, other.mRemoteInput);
Adrian Roosaf06bf22016-07-15 12:26:49 -0700369 setRevealParameters(other.mRevealCx, other.mRevealCy, other.mRevealR);
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700370 focus();
371 }
372
373 /**
374 * Tries to find an action in {@param actions} that matches the current pending intent
375 * of this view and updates its state to that of the found action
376 *
377 * @return true if a matching action was found, false otherwise
378 */
379 public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700380 if (mPendingIntent == null || actions == null) {
381 return false;
382 }
383 Intent current = mPendingIntent.getIntent();
384 if (current == null) {
385 return false;
386 }
387
388 for (Notification.Action a : actions) {
389 RemoteInput[] inputs = a.getRemoteInputs();
390 if (a.actionIntent == null || inputs == null) {
391 continue;
392 }
393 Intent candidate = a.actionIntent.getIntent();
394 if (!current.filterEquals(candidate)) {
395 continue;
396 }
397
398 RemoteInput input = null;
399 for (RemoteInput i : inputs) {
400 if (i.getAllowFreeFormInput()) {
401 input = i;
402 }
403 }
404 if (input == null) {
405 continue;
406 }
407 setPendingIntent(a.actionIntent);
408 setRemoteInput(inputs, input);
409 return true;
410 }
411 return false;
412 }
413
414 public PendingIntent getPendingIntent() {
415 return mPendingIntent;
416 }
417
Adrian Roosd009ab12016-05-20 17:58:53 -0700418 public void setRemoved() {
419 mRemoved = true;
420 }
421
Adrian Roosaf06bf22016-07-15 12:26:49 -0700422 public void setRevealParameters(int cx, int cy, int r) {
423 mRevealCx = cx;
424 mRevealCy = cy;
425 mRevealR = r;
426 }
427
Adrian Roos7813dd72016-09-23 17:12:17 -0700428 @Override
429 public void dispatchStartTemporaryDetach() {
430 super.dispatchStartTemporaryDetach();
431 // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and
432 // won't lose IME focus.
433 detachViewFromParent(mEditText);
434 }
435
436 @Override
437 public void dispatchFinishTemporaryDetach() {
438 if (isAttachedToWindow()) {
439 attachViewToParent(mEditText, 0, mEditText.getLayoutParams());
440 } else {
441 removeDetachedView(mEditText, false /* animate */);
442 }
443 super.dispatchFinishTemporaryDetach();
444 }
445
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800446 public void setWrapper(NotificationViewWrapper wrapper) {
447 mWrapper = wrapper;
448 }
449
Adrian Roos497ab022015-02-10 20:49:33 +0100450 /**
451 * An EditText that changes appearance based on whether it's focusable and becomes
452 * un-focusable whenever the user navigates away from it or it becomes invisible.
453 */
454 public static class RemoteEditText extends EditText {
455
456 private final Drawable mBackground;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700457 private RemoteInputView mRemoteInputView;
Adrian Roos1c0ca502015-10-07 12:20:42 -0700458 boolean mShowImeOnInputConnection;
Adrian Roos497ab022015-02-10 20:49:33 +0100459
460 public RemoteEditText(Context context, AttributeSet attrs) {
461 super(context, attrs);
462 mBackground = getBackground();
463 }
464
Adrian Roosaf06bf22016-07-15 12:26:49 -0700465 private void defocusIfNeeded(boolean animate) {
Adrian Roos7813dd72016-09-23 17:12:17 -0700466 if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()
467 || isTemporarilyDetached()) {
468 if (isTemporarilyDetached()) {
469 // We might get reattached but then the other one of HUN / expanded might steal
470 // our focus, so we'll need to save our text here.
471 if (mRemoteInputView != null) {
472 mRemoteInputView.mEntry.remoteInputText = getText();
473 }
474 }
Adrian Roos14503e22016-03-09 14:01:24 -0800475 return;
476 }
Adrian Roos497ab022015-02-10 20:49:33 +0100477 if (isFocusable() && isEnabled()) {
478 setInnerFocusable(false);
Adrian Roos5153d4a2016-03-22 10:01:56 -0700479 if (mRemoteInputView != null) {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700480 mRemoteInputView.onDefocus(animate);
Adrian Roos1c0ca502015-10-07 12:20:42 -0700481 }
482 mShowImeOnInputConnection = false;
Adrian Roos497ab022015-02-10 20:49:33 +0100483 }
484 }
485
486 @Override
487 protected void onVisibilityChanged(View changedView, int visibility) {
488 super.onVisibilityChanged(changedView, visibility);
489
490 if (!isShown()) {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700491 defocusIfNeeded(false /* animate */);
Adrian Roos497ab022015-02-10 20:49:33 +0100492 }
493 }
494
495 @Override
Adrian Roos967f6192016-03-09 12:47:26 -0800496 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
497 super.onFocusChanged(focused, direction, previouslyFocusedRect);
498 if (!focused) {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700499 defocusIfNeeded(true /* animate */);
Adrian Roos967f6192016-03-09 12:47:26 -0800500 }
Adrian Roos497ab022015-02-10 20:49:33 +0100501 }
502
503 @Override
Adrian Roosdc5b4532016-01-06 20:49:41 +0100504 public void getFocusedRect(Rect r) {
505 super.getFocusedRect(r);
506 r.top = mScrollY;
507 r.bottom = mScrollY + (mBottom - mTop);
508 }
509
510 @Override
Adrian Roos5153d4a2016-03-22 10:01:56 -0700511 public boolean requestRectangleOnScreen(Rect rectangle) {
512 return mRemoteInputView.requestScrollTo();
513 }
514
515 @Override
Adrian Roos56cf73a2016-07-18 14:23:32 -0700516 public boolean onKeyDown(int keyCode, KeyEvent event) {
517 if (keyCode == KeyEvent.KEYCODE_BACK) {
518 // Eat the DOWN event here to prevent any default behavior.
Adrian Roos777ef562015-12-01 17:37:14 -0800519 return true;
Adrian Roos497ab022015-02-10 20:49:33 +0100520 }
Adrian Roos56cf73a2016-07-18 14:23:32 -0700521 return super.onKeyDown(keyCode, event);
522 }
523
524 @Override
525 public boolean onKeyUp(int keyCode, KeyEvent event) {
526 if (keyCode == KeyEvent.KEYCODE_BACK) {
527 defocusIfNeeded(true /* animate */);
528 return true;
529 }
530 return super.onKeyUp(keyCode, event);
Adrian Roos497ab022015-02-10 20:49:33 +0100531 }
532
Adrian Roos1c0ca502015-10-07 12:20:42 -0700533 @Override
Adrian Roosd009ab12016-05-20 17:58:53 -0700534 public boolean onCheckIsTextEditor() {
535 // Stop being editable while we're being removed. During removal, we get reattached,
536 // and editable views get their spellchecking state re-evaluated which is too costly
537 // during the removal animation.
538 boolean flyingOut = mRemoteInputView != null && mRemoteInputView.mRemoved;
539 return !flyingOut && super.onCheckIsTextEditor();
540 }
541
542 @Override
Adrian Roos1c0ca502015-10-07 12:20:42 -0700543 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
544 final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
545
546 if (mShowImeOnInputConnection && inputConnection != null) {
547 final InputMethodManager imm = InputMethodManager.getInstance();
548 if (imm != null) {
549 // onCreateInputConnection is called by InputMethodManager in the middle of
550 // setting up the connection to the IME; wait with requesting the IME until that
551 // work has completed.
552 post(new Runnable() {
553 @Override
554 public void run() {
555 imm.viewClicked(RemoteEditText.this);
556 imm.showSoftInput(RemoteEditText.this, 0);
557 }
558 });
559 }
560 }
561
562 return inputConnection;
563 }
Adrian Roos497ab022015-02-10 20:49:33 +0100564
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800565 @Override
566 public void onCommitCompletion(CompletionInfo text) {
567 clearComposingText();
568 setText(text.getText());
569 setSelection(getText().length());
570 }
571
Adrian Roos497ab022015-02-10 20:49:33 +0100572 void setInnerFocusable(boolean focusable) {
573 setFocusableInTouchMode(focusable);
574 setFocusable(focusable);
575 setCursorVisible(focusable);
576
577 if (focusable) {
578 requestFocus();
579 setBackground(mBackground);
580 } else {
581 setBackground(null);
582 }
583
584 }
585 }
586}