blob: 179c0d57aa50e3f3d38fe5564d24bce12f6b31d1 [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
Selim Cinekde4de0e2018-01-24 16:21:07 -0800172 public CharSequence getText() {
173 return mEditText.getText();
174 }
175
Adrian Roos497ab022015-02-10 20:49:33 +0100176 public static RemoteInputView inflate(Context context, ViewGroup root,
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800177 NotificationData.Entry entry,
Adrian Roos1c0ca502015-10-07 12:20:42 -0700178 RemoteInputController controller) {
Adrian Roos497ab022015-02-10 20:49:33 +0100179 RemoteInputView v = (RemoteInputView)
180 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
Adrian Roos1c0ca502015-10-07 12:20:42 -0700181 v.mController = controller;
182 v.mEntry = entry;
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800183 v.setTag(VIEW_TAG);
Adrian Roos497ab022015-02-10 20:49:33 +0100184
185 return v;
186 }
187
188 @Override
189 public void onClick(View v) {
Adrian Roos5389a172016-04-20 13:58:03 -0700190 if (v == mSendButton) {
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800191 sendRemoteInput();
Adrian Roos497ab022015-02-10 20:49:33 +0100192 }
193 }
194
Adrian Roos5389a172016-04-20 13:58:03 -0700195 @Override
196 public boolean onTouchEvent(MotionEvent event) {
197 super.onTouchEvent(event);
198
199 // We never want for a touch to escape to an outer view or one we covered.
200 return true;
201 }
202
Adrian Roosaf06bf22016-07-15 12:26:49 -0700203 private void onDefocus(boolean animate) {
Adrian Roos7813dd72016-09-23 17:12:17 -0700204 mController.removeRemoteInput(mEntry, mToken);
Adrian Roos777ef562015-12-01 17:37:14 -0800205 mEntry.remoteInputText = mEditText.getText();
Adrian Roosd009ab12016-05-20 17:58:53 -0700206
207 // During removal, we get reattached and lose focus. Not hiding in that
208 // case to prevent flicker.
209 if (!mRemoved) {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700210 if (animate && mRevealR > 0) {
211 Animator reveal = ViewAnimationUtils.createCircularReveal(
212 this, mRevealCx, mRevealCy, mRevealR, 0);
213 reveal.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
214 reveal.setDuration(StackStateAnimator.ANIMATION_DURATION_CLOSE_REMOTE_INPUT);
215 reveal.addListener(new AnimatorListenerAdapter() {
216 @Override
217 public void onAnimationEnd(Animator animation) {
218 setVisibility(INVISIBLE);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800219 if (mWrapper != null) {
220 mWrapper.setRemoteInputVisible(false);
221 }
Adrian Roosaf06bf22016-07-15 12:26:49 -0700222 }
223 });
224 reveal.start();
225 } else {
226 setVisibility(INVISIBLE);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800227 if (mWrapper != null) {
228 mWrapper.setRemoteInputVisible(false);
229 }
Adrian Roosaf06bf22016-07-15 12:26:49 -0700230 }
Adrian Roosd009ab12016-05-20 17:58:53 -0700231 }
Adrian Roosceeb04c2016-04-25 14:00:54 -0700232 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
233 mEntry.notification.getPackageName());
Adrian Roos1c0ca502015-10-07 12:20:42 -0700234 }
235
236 @Override
Adrian Roos14503e22016-03-09 14:01:24 -0800237 protected void onAttachedToWindow() {
238 super.onAttachedToWindow();
239 if (mEntry.row.isChangingPosition()) {
240 if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
241 mEditText.requestFocus();
242 }
243 }
244 }
245
246 @Override
Adrian Roos1c0ca502015-10-07 12:20:42 -0700247 protected void onDetachedFromWindow() {
248 super.onDetachedFromWindow();
Adrian Roos7813dd72016-09-23 17:12:17 -0700249 if (mEntry.row.isChangingPosition() || isTemporarilyDetached()) {
Adrian Roos14503e22016-03-09 14:01:24 -0800250 return;
251 }
Adrian Roos7813dd72016-09-23 17:12:17 -0700252 mController.removeRemoteInput(mEntry, mToken);
253 mController.removeSpinning(mEntry.key, mToken);
Adrian Roos1c0ca502015-10-07 12:20:42 -0700254 }
255
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800256 public void setPendingIntent(PendingIntent pendingIntent) {
257 mPendingIntent = pendingIntent;
258 }
259
260 public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
261 mRemoteInputs = remoteInputs;
262 mRemoteInput = remoteInput;
263 mEditText.setHint(mRemoteInput.getLabel());
264 }
265
Adrian Roosaf06bf22016-07-15 12:26:49 -0700266 public void focusAnimated() {
267 if (getVisibility() != VISIBLE) {
268 Animator animator = ViewAnimationUtils.createCircularReveal(
269 this, mRevealCx, mRevealCy, 0, mRevealR);
270 animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
271 animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
272 animator.start();
273 }
274 focus();
275 }
276
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800277 public void focus() {
Adrian Roosceeb04c2016-04-25 14:00:54 -0700278 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
279 mEntry.notification.getPackageName());
280
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700281 setVisibility(VISIBLE);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800282 if (mWrapper != null) {
283 mWrapper.setRemoteInputVisible(true);
284 }
Adrian Roos7813dd72016-09-23 17:12:17 -0700285 mController.addRemoteInput(mEntry, mToken);
Adrian Roosdc5b4532016-01-06 20:49:41 +0100286 mEditText.setInnerFocusable(true);
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800287 mEditText.mShowImeOnInputConnection = true;
Adrian Roos777ef562015-12-01 17:37:14 -0800288 mEditText.setText(mEntry.remoteInputText);
289 mEditText.setSelection(mEditText.getText().length());
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800290 mEditText.requestFocus();
Adrian Roos245aa872015-12-07 14:53:53 -0800291 updateSendButton();
292 }
293
Adrian Roos0789ee02016-06-01 11:34:58 -0700294 public void onNotificationUpdateOrReset() {
Adrian Roos245aa872015-12-07 14:53:53 -0800295 boolean sending = mProgressBar.getVisibility() == VISIBLE;
296
297 if (sending) {
298 // Update came in after we sent the reply, time to reset.
299 reset();
300 }
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800301
302 if (isActive() && mWrapper != null) {
303 mWrapper.setRemoteInputVisible(true);
304 }
Adrian Roos245aa872015-12-07 14:53:53 -0800305 }
306
307 private void reset() {
Adrian Roosf17c86b2016-09-07 14:08:40 -0700308 mResetting = true;
Selim Cinek1397ea32018-01-16 17:34:52 -0800309 mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText());
Adrian Roosf17c86b2016-09-07 14:08:40 -0700310
Adrian Roos245aa872015-12-07 14:53:53 -0800311 mEditText.getText().clear();
312 mEditText.setEnabled(true);
313 mSendButton.setVisibility(VISIBLE);
314 mProgressBar.setVisibility(INVISIBLE);
Adrian Roos7813dd72016-09-23 17:12:17 -0700315 mController.removeSpinning(mEntry.key, mToken);
Adrian Roos245aa872015-12-07 14:53:53 -0800316 updateSendButton();
Adrian Roosaf06bf22016-07-15 12:26:49 -0700317 onDefocus(false /* animate */);
Adrian Roosf17c86b2016-09-07 14:08:40 -0700318
319 mResetting = false;
320 }
321
322 @Override
323 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
324 if (mResetting && child == mEditText) {
325 // Suppress text events if it happens during resetting. Ideally this would be
326 // suppressed by the text view not being shown, but that doesn't work here because it
327 // needs to stay visible for the animation.
328 return false;
329 }
330 return super.onRequestSendAccessibilityEvent(child, event);
Adrian Roos245aa872015-12-07 14:53:53 -0800331 }
332
333 private void updateSendButton() {
334 mSendButton.setEnabled(mEditText.getText().length() != 0);
335 }
336
337 @Override
338 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
339
340 @Override
341 public void onTextChanged(CharSequence s, int start, int before, int count) {}
342
343 @Override
344 public void afterTextChanged(Editable s) {
345 updateSendButton();
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800346 }
347
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700348 public void close() {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700349 mEditText.defocusIfNeeded(false /* animated */);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700350 }
351
352 @Override
353 public boolean onInterceptTouchEvent(MotionEvent ev) {
354 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
yoshiki iguchiff4696c2017-11-22 16:47:41 +0900355 mController.requestDisallowLongPressAndDismiss();
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700356 }
357 return super.onInterceptTouchEvent(ev);
358 }
359
Adrian Roos5153d4a2016-03-22 10:01:56 -0700360 public boolean requestScrollTo() {
yoshiki iguchiff4696c2017-11-22 16:47:41 +0900361 mController.lockScrollTo(mEntry);
Adrian Roos5153d4a2016-03-22 10:01:56 -0700362 return true;
363 }
364
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700365 public boolean isActive() {
Adrian Roos9550f2e2016-08-23 18:23:01 +0200366 return mEditText.isFocused() && mEditText.isEnabled();
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700367 }
368
369 public void stealFocusFrom(RemoteInputView other) {
370 other.close();
371 setPendingIntent(other.mPendingIntent);
372 setRemoteInput(other.mRemoteInputs, other.mRemoteInput);
Adrian Roosaf06bf22016-07-15 12:26:49 -0700373 setRevealParameters(other.mRevealCx, other.mRevealCy, other.mRevealR);
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700374 focus();
375 }
376
377 /**
378 * Tries to find an action in {@param actions} that matches the current pending intent
379 * of this view and updates its state to that of the found action
380 *
381 * @return true if a matching action was found, false otherwise
382 */
383 public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700384 if (mPendingIntent == null || actions == null) {
385 return false;
386 }
387 Intent current = mPendingIntent.getIntent();
388 if (current == null) {
389 return false;
390 }
391
392 for (Notification.Action a : actions) {
393 RemoteInput[] inputs = a.getRemoteInputs();
394 if (a.actionIntent == null || inputs == null) {
395 continue;
396 }
397 Intent candidate = a.actionIntent.getIntent();
398 if (!current.filterEquals(candidate)) {
399 continue;
400 }
401
402 RemoteInput input = null;
403 for (RemoteInput i : inputs) {
404 if (i.getAllowFreeFormInput()) {
405 input = i;
406 }
407 }
408 if (input == null) {
409 continue;
410 }
411 setPendingIntent(a.actionIntent);
412 setRemoteInput(inputs, input);
413 return true;
414 }
415 return false;
416 }
417
418 public PendingIntent getPendingIntent() {
419 return mPendingIntent;
420 }
421
Adrian Roosd009ab12016-05-20 17:58:53 -0700422 public void setRemoved() {
423 mRemoved = true;
424 }
425
Adrian Roosaf06bf22016-07-15 12:26:49 -0700426 public void setRevealParameters(int cx, int cy, int r) {
427 mRevealCx = cx;
428 mRevealCy = cy;
429 mRevealR = r;
430 }
431
Adrian Roos7813dd72016-09-23 17:12:17 -0700432 @Override
433 public void dispatchStartTemporaryDetach() {
434 super.dispatchStartTemporaryDetach();
435 // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and
436 // won't lose IME focus.
437 detachViewFromParent(mEditText);
438 }
439
440 @Override
441 public void dispatchFinishTemporaryDetach() {
442 if (isAttachedToWindow()) {
443 attachViewToParent(mEditText, 0, mEditText.getLayoutParams());
444 } else {
445 removeDetachedView(mEditText, false /* animate */);
446 }
447 super.dispatchFinishTemporaryDetach();
448 }
449
Adrian Roos7b9ed0d2017-01-24 15:55:18 -0800450 public void setWrapper(NotificationViewWrapper wrapper) {
451 mWrapper = wrapper;
452 }
453
Adrian Roos497ab022015-02-10 20:49:33 +0100454 /**
455 * An EditText that changes appearance based on whether it's focusable and becomes
456 * un-focusable whenever the user navigates away from it or it becomes invisible.
457 */
458 public static class RemoteEditText extends EditText {
459
460 private final Drawable mBackground;
Adrian Roos5153d4a2016-03-22 10:01:56 -0700461 private RemoteInputView mRemoteInputView;
Adrian Roos1c0ca502015-10-07 12:20:42 -0700462 boolean mShowImeOnInputConnection;
Adrian Roos497ab022015-02-10 20:49:33 +0100463
464 public RemoteEditText(Context context, AttributeSet attrs) {
465 super(context, attrs);
466 mBackground = getBackground();
467 }
468
Adrian Roosaf06bf22016-07-15 12:26:49 -0700469 private void defocusIfNeeded(boolean animate) {
Adrian Roos7813dd72016-09-23 17:12:17 -0700470 if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()
471 || isTemporarilyDetached()) {
472 if (isTemporarilyDetached()) {
473 // We might get reattached but then the other one of HUN / expanded might steal
474 // our focus, so we'll need to save our text here.
475 if (mRemoteInputView != null) {
476 mRemoteInputView.mEntry.remoteInputText = getText();
477 }
478 }
Adrian Roos14503e22016-03-09 14:01:24 -0800479 return;
480 }
Adrian Roos497ab022015-02-10 20:49:33 +0100481 if (isFocusable() && isEnabled()) {
482 setInnerFocusable(false);
Adrian Roos5153d4a2016-03-22 10:01:56 -0700483 if (mRemoteInputView != null) {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700484 mRemoteInputView.onDefocus(animate);
Adrian Roos1c0ca502015-10-07 12:20:42 -0700485 }
486 mShowImeOnInputConnection = false;
Adrian Roos497ab022015-02-10 20:49:33 +0100487 }
488 }
489
490 @Override
491 protected void onVisibilityChanged(View changedView, int visibility) {
492 super.onVisibilityChanged(changedView, visibility);
493
494 if (!isShown()) {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700495 defocusIfNeeded(false /* animate */);
Adrian Roos497ab022015-02-10 20:49:33 +0100496 }
497 }
498
499 @Override
Adrian Roos967f6192016-03-09 12:47:26 -0800500 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
501 super.onFocusChanged(focused, direction, previouslyFocusedRect);
502 if (!focused) {
Adrian Roosaf06bf22016-07-15 12:26:49 -0700503 defocusIfNeeded(true /* animate */);
Adrian Roos967f6192016-03-09 12:47:26 -0800504 }
Adrian Roos497ab022015-02-10 20:49:33 +0100505 }
506
507 @Override
Adrian Roosdc5b4532016-01-06 20:49:41 +0100508 public void getFocusedRect(Rect r) {
509 super.getFocusedRect(r);
510 r.top = mScrollY;
511 r.bottom = mScrollY + (mBottom - mTop);
512 }
513
514 @Override
Adrian Roos5153d4a2016-03-22 10:01:56 -0700515 public boolean requestRectangleOnScreen(Rect rectangle) {
516 return mRemoteInputView.requestScrollTo();
517 }
518
519 @Override
Adrian Roos56cf73a2016-07-18 14:23:32 -0700520 public boolean onKeyDown(int keyCode, KeyEvent event) {
521 if (keyCode == KeyEvent.KEYCODE_BACK) {
522 // Eat the DOWN event here to prevent any default behavior.
Adrian Roos777ef562015-12-01 17:37:14 -0800523 return true;
Adrian Roos497ab022015-02-10 20:49:33 +0100524 }
Adrian Roos56cf73a2016-07-18 14:23:32 -0700525 return super.onKeyDown(keyCode, event);
526 }
527
528 @Override
529 public boolean onKeyUp(int keyCode, KeyEvent event) {
530 if (keyCode == KeyEvent.KEYCODE_BACK) {
531 defocusIfNeeded(true /* animate */);
532 return true;
533 }
534 return super.onKeyUp(keyCode, event);
Adrian Roos497ab022015-02-10 20:49:33 +0100535 }
536
Adrian Roos1c0ca502015-10-07 12:20:42 -0700537 @Override
Adrian Roosd009ab12016-05-20 17:58:53 -0700538 public boolean onCheckIsTextEditor() {
539 // Stop being editable while we're being removed. During removal, we get reattached,
540 // and editable views get their spellchecking state re-evaluated which is too costly
541 // during the removal animation.
542 boolean flyingOut = mRemoteInputView != null && mRemoteInputView.mRemoved;
543 return !flyingOut && super.onCheckIsTextEditor();
544 }
545
546 @Override
Adrian Roos1c0ca502015-10-07 12:20:42 -0700547 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
548 final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
549
550 if (mShowImeOnInputConnection && inputConnection != null) {
551 final InputMethodManager imm = InputMethodManager.getInstance();
552 if (imm != null) {
553 // onCreateInputConnection is called by InputMethodManager in the middle of
554 // setting up the connection to the IME; wait with requesting the IME until that
555 // work has completed.
556 post(new Runnable() {
557 @Override
558 public void run() {
559 imm.viewClicked(RemoteEditText.this);
560 imm.showSoftInput(RemoteEditText.this, 0);
561 }
562 });
563 }
564 }
565
566 return inputConnection;
567 }
Adrian Roos497ab022015-02-10 20:49:33 +0100568
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800569 @Override
570 public void onCommitCompletion(CompletionInfo text) {
571 clearComposingText();
572 setText(text.getText());
573 setSelection(getText().length());
574 }
575
Adrian Roos497ab022015-02-10 20:49:33 +0100576 void setInnerFocusable(boolean focusable) {
577 setFocusableInTouchMode(focusable);
578 setFocusable(focusable);
579 setCursorVisible(focusable);
580
581 if (focusable) {
582 requestFocus();
583 setBackground(mBackground);
584 } else {
585 setBackground(null);
586 }
587
588 }
589 }
590}