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