1/* 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 19import com.android.systemui.R; 20 21import android.annotation.NonNull; 22import android.app.Notification; 23import android.app.PendingIntent; 24import android.app.RemoteInput; 25import android.content.Context; 26import android.content.Intent; 27import android.graphics.drawable.Drawable; 28import android.os.Bundle; 29import android.util.AttributeSet; 30import android.util.Log; 31import android.view.KeyEvent; 32import android.view.LayoutInflater; 33import android.view.View; 34import android.view.ViewGroup; 35import android.view.inputmethod.EditorInfo; 36import android.view.inputmethod.InputMethodManager; 37import android.widget.EditText; 38import android.widget.FrameLayout; 39import android.widget.ProgressBar; 40import android.widget.TextView; 41 42/** 43 * Host for the remote input. 44 */ 45public class RemoteInputView extends FrameLayout implements View.OnClickListener { 46 47 private static final String TAG = "RemoteInput"; 48 49 private RemoteEditText mEditText; 50 private ProgressBar mProgressBar; 51 private PendingIntent mPendingIntent; 52 private RemoteInput mRemoteInput; 53 private Notification.Action mAction; 54 55 public RemoteInputView(Context context, AttributeSet attrs) { 56 super(context, attrs); 57 } 58 59 @Override 60 protected void onFinishInflate() { 61 super.onFinishInflate(); 62 63 mProgressBar = (ProgressBar) findViewById(R.id.remote_input_progress); 64 65 mEditText = (RemoteEditText) getChildAt(0); 66 mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { 67 @Override 68 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 69 70 // Check if this was the result of hitting the enter key 71 final boolean isSoftImeEvent = event == null 72 && (actionId == EditorInfo.IME_ACTION_DONE 73 || actionId == EditorInfo.IME_ACTION_NEXT 74 || actionId == EditorInfo.IME_ACTION_SEND); 75 final boolean isKeyboardEnterKey = event != null 76 && KeyEvent.isConfirmKey(event.getKeyCode()) 77 && event.getAction() == KeyEvent.ACTION_DOWN; 78 79 if (isSoftImeEvent || isKeyboardEnterKey) { 80 sendRemoteInput(); 81 return true; 82 } 83 return false; 84 } 85 }); 86 mEditText.setOnClickListener(this); 87 mEditText.setInnerFocusable(false); 88 } 89 90 private void sendRemoteInput() { 91 Bundle results = new Bundle(); 92 results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString()); 93 Intent fillInIntent = new Intent(); 94 RemoteInput.addResultsToIntent(mAction.getRemoteInputs(), fillInIntent, 95 results); 96 97 mEditText.setEnabled(false); 98 mProgressBar.setVisibility(VISIBLE); 99 100 try { 101 mPendingIntent.send(mContext, 0, fillInIntent); 102 } catch (PendingIntent.CanceledException e) { 103 Log.i(TAG, "Unable to send remote input result", e); 104 } 105 } 106 107 public static RemoteInputView inflate(Context context, ViewGroup root, 108 Notification.Action action, RemoteInput remoteInput) { 109 RemoteInputView v = (RemoteInputView) 110 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false); 111 112 v.mEditText.setHint(action.title); 113 v.mPendingIntent = action.actionIntent; 114 v.mRemoteInput = remoteInput; 115 v.mAction = action; 116 117 return v; 118 } 119 120 @Override 121 public void onClick(View v) { 122 if (v == mEditText) { 123 if (!mEditText.isFocusable()) { 124 mEditText.setInnerFocusable(true); 125 InputMethodManager imm = InputMethodManager.getInstance(); 126 if (imm != null) { 127 imm.viewClicked(mEditText); 128 imm.showSoftInput(mEditText, 0); 129 } 130 } 131 } 132 } 133 134 /** 135 * An EditText that changes appearance based on whether it's focusable and becomes 136 * un-focusable whenever the user navigates away from it or it becomes invisible. 137 */ 138 public static class RemoteEditText extends EditText { 139 140 private final Drawable mBackground; 141 142 public RemoteEditText(Context context, AttributeSet attrs) { 143 super(context, attrs); 144 mBackground = getBackground(); 145 } 146 147 private void defocusIfNeeded() { 148 if (isFocusable() && isEnabled()) { 149 setInnerFocusable(false); 150 } 151 } 152 153 @Override 154 protected void onVisibilityChanged(View changedView, int visibility) { 155 super.onVisibilityChanged(changedView, visibility); 156 157 if (!isShown()) { 158 defocusIfNeeded(); 159 } 160 } 161 162 @Override 163 protected void onFocusLost() { 164 super.onFocusLost(); 165 defocusIfNeeded(); 166 } 167 168 @Override 169 public boolean onKeyPreIme(int keyCode, KeyEvent event) { 170 if (keyCode == KeyEvent.KEYCODE_BACK) { 171 defocusIfNeeded(); 172 } 173 return super.onKeyPreIme(keyCode, event); 174 } 175 176 177 void setInnerFocusable(boolean focusable) { 178 setFocusableInTouchMode(focusable); 179 setFocusable(focusable); 180 setCursorVisible(focusable); 181 182 if (focusable) { 183 requestFocus(); 184 setBackground(mBackground); 185 } else { 186 setBackground(null); 187 } 188 189 } 190 } 191} 192