1/*
2 * Copyright (C) 2013 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.incallui;
18
19import android.app.AlertDialog;
20import android.app.Dialog;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.os.Bundle;
24import android.text.Editable;
25import android.text.TextWatcher;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.ViewGroup;
29import android.view.WindowManager;
30import android.widget.AdapterView;
31import android.widget.ArrayAdapter;
32import android.widget.Button;
33import android.widget.EditText;
34import android.widget.ListView;
35
36import com.google.common.base.Preconditions;
37import com.google.common.collect.Lists;
38
39import java.util.ArrayList;
40import java.util.List;
41
42/**
43 *
44 */
45public class AnswerFragment extends BaseFragment<AnswerPresenter, AnswerPresenter.AnswerUi>
46        implements GlowPadWrapper.AnswerListener, AnswerPresenter.AnswerUi {
47
48    public static final int TARGET_SET_FOR_AUDIO_WITHOUT_SMS = 0;
49    public static final int TARGET_SET_FOR_AUDIO_WITH_SMS = 1;
50    public static final int TARGET_SET_FOR_VIDEO_WITHOUT_SMS = 2;
51    public static final int TARGET_SET_FOR_VIDEO_WITH_SMS = 3;
52    public static final int TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST = 4;
53
54    /**
55     * The popup showing the list of canned responses.
56     *
57     * This is an AlertDialog containing a ListView showing the possible choices.  This may be null
58     * if the InCallScreen hasn't ever called showRespondViaSmsPopup() yet, or if the popup was
59     * visible once but then got dismissed.
60     */
61    private Dialog mCannedResponsePopup = null;
62
63    /**
64     * The popup showing a text field for users to type in their custom message.
65     */
66    private AlertDialog mCustomMessagePopup = null;
67
68    private ArrayAdapter<String> mSmsResponsesAdapter;
69
70    private final List<String> mSmsResponses = new ArrayList<>();
71
72    private GlowPadWrapper mGlowpad;
73
74    public AnswerFragment() {
75    }
76
77    @Override
78    public AnswerPresenter createPresenter() {
79        return new AnswerPresenter();
80    }
81
82    @Override
83    AnswerPresenter.AnswerUi getUi() {
84        return this;
85    }
86
87    @Override
88    public View onCreateView(LayoutInflater inflater, ViewGroup container,
89            Bundle savedInstanceState) {
90        mGlowpad = (GlowPadWrapper) inflater.inflate(R.layout.answer_fragment,
91                container, false);
92
93        Log.d(this, "Creating view for answer fragment ", this);
94        Log.d(this, "Created from activity", getActivity());
95        mGlowpad.setAnswerListener(this);
96
97        return mGlowpad;
98    }
99
100    @Override
101    public void onDestroyView() {
102        Log.d(this, "onDestroyView");
103        if (mGlowpad != null) {
104            mGlowpad.stopPing();
105            mGlowpad = null;
106        }
107        super.onDestroyView();
108    }
109
110    @Override
111    public void showAnswerUi(boolean show) {
112        getView().setVisibility(show ? View.VISIBLE : View.GONE);
113
114        Log.d(this, "Show answer UI: " + show);
115        if (show) {
116            mGlowpad.startPing();
117        } else {
118            mGlowpad.stopPing();
119        }
120    }
121
122    /**
123     * Sets targets on the glowpad according to target set identified by the parameter.
124     * @param targetSet Integer identifying the set of targets to use.
125     */
126    @Override
127    public void showTargets(int targetSet) {
128        final int targetResourceId;
129        final int targetDescriptionsResourceId;
130        final int directionDescriptionsResourceId;
131        final int handleDrawableResourceId;
132
133        switch (targetSet) {
134            case TARGET_SET_FOR_AUDIO_WITH_SMS:
135                targetResourceId = R.array.incoming_call_widget_audio_with_sms_targets;
136                targetDescriptionsResourceId =
137                        R.array.incoming_call_widget_audio_with_sms_target_descriptions;
138                directionDescriptionsResourceId =
139                        R.array.incoming_call_widget_audio_with_sms_direction_descriptions;
140                handleDrawableResourceId = R.drawable.ic_incall_audio_handle;
141                break;
142            case TARGET_SET_FOR_VIDEO_WITHOUT_SMS:
143                targetResourceId = R.array.incoming_call_widget_video_without_sms_targets;
144                targetDescriptionsResourceId =
145                        R.array.incoming_call_widget_video_without_sms_target_descriptions;
146                directionDescriptionsResourceId =
147                        R.array.incoming_call_widget_video_without_sms_direction_descriptions;
148                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
149                break;
150            case TARGET_SET_FOR_VIDEO_WITH_SMS:
151                targetResourceId = R.array.incoming_call_widget_video_with_sms_targets;
152                targetDescriptionsResourceId =
153                        R.array.incoming_call_widget_video_with_sms_target_descriptions;
154                directionDescriptionsResourceId =
155                        R.array.incoming_call_widget_video_with_sms_direction_descriptions;
156                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
157                break;
158            case TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST:
159                targetResourceId = R.array.incoming_call_widget_video_upgrade_request_targets;
160                targetDescriptionsResourceId =
161                        R.array.incoming_call_widget_video_upgrade_request_target_descriptions;
162                directionDescriptionsResourceId = R.array
163                        .incoming_call_widget_video_upgrade_request_target_direction_descriptions;
164                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
165                break;
166            case TARGET_SET_FOR_AUDIO_WITHOUT_SMS:
167            default:
168                targetResourceId = R.array.incoming_call_widget_audio_without_sms_targets;
169                targetDescriptionsResourceId =
170                        R.array.incoming_call_widget_audio_without_sms_target_descriptions;
171                directionDescriptionsResourceId =
172                        R.array.incoming_call_widget_audio_without_sms_direction_descriptions;
173                handleDrawableResourceId = R.drawable.ic_incall_audio_handle;
174                break;
175        }
176
177        if (targetResourceId != mGlowpad.getTargetResourceId()) {
178            mGlowpad.setTargetResources(targetResourceId);
179            mGlowpad.setTargetDescriptionsResourceId(targetDescriptionsResourceId);
180            mGlowpad.setDirectionDescriptionsResourceId(directionDescriptionsResourceId);
181            mGlowpad.setHandleDrawable(handleDrawableResourceId);
182            mGlowpad.reset(false);
183        }
184    }
185
186    @Override
187    public void showMessageDialog() {
188        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
189
190        mSmsResponsesAdapter = new ArrayAdapter<>(builder.getContext(),
191                android.R.layout.simple_list_item_1, android.R.id.text1, mSmsResponses);
192
193        final ListView lv = new ListView(getActivity());
194        lv.setAdapter(mSmsResponsesAdapter);
195        lv.setOnItemClickListener(new RespondViaSmsItemClickListener());
196
197        builder.setCancelable(true).setView(lv).setOnCancelListener(
198                new DialogInterface.OnCancelListener() {
199                    @Override
200                    public void onCancel(DialogInterface dialogInterface) {
201                        if (mGlowpad != null) {
202                            mGlowpad.startPing();
203                        }
204                        dismissCannedResponsePopup();
205                        getPresenter().onDismissDialog();
206                    }
207                });
208        mCannedResponsePopup = builder.create();
209        mCannedResponsePopup.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
210        mCannedResponsePopup.show();
211    }
212
213    private boolean isCannedResponsePopupShowing() {
214        if (mCannedResponsePopup != null) {
215            return mCannedResponsePopup.isShowing();
216        }
217        return false;
218    }
219
220    private boolean isCustomMessagePopupShowing() {
221        if (mCustomMessagePopup != null) {
222            return mCustomMessagePopup.isShowing();
223        }
224        return false;
225    }
226
227    /**
228     * Dismiss the canned response list popup.
229     *
230     * This is safe to call even if the popup is already dismissed, and even if you never called
231     * showRespondViaSmsPopup() in the first place.
232     */
233    private void dismissCannedResponsePopup() {
234        if (mCannedResponsePopup != null) {
235            mCannedResponsePopup.dismiss();  // safe even if already dismissed
236            mCannedResponsePopup = null;
237        }
238    }
239
240    /**
241     * Dismiss the custom compose message popup.
242     */
243    private void dismissCustomMessagePopup() {
244       if (mCustomMessagePopup != null) {
245           mCustomMessagePopup.dismiss();
246           mCustomMessagePopup = null;
247       }
248    }
249
250    public void dismissPendingDialogues() {
251        if (isCannedResponsePopupShowing()) {
252            dismissCannedResponsePopup();
253        }
254
255        if (isCustomMessagePopupShowing()) {
256            dismissCustomMessagePopup();
257        }
258    }
259
260    public boolean hasPendingDialogs() {
261        return !(mCannedResponsePopup == null && mCustomMessagePopup == null);
262    }
263
264    /**
265     * Shows the custom message entry dialog.
266     */
267    public void showCustomMessageDialog() {
268        // Create an alert dialog containing an EditText
269        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
270        final EditText et = new EditText(builder.getContext());
271        builder.setCancelable(true).setView(et)
272                .setPositiveButton(R.string.custom_message_send,
273                        new DialogInterface.OnClickListener() {
274                    @Override
275                    public void onClick(DialogInterface dialog, int which) {
276                        // The order is arranged in a way that the popup will be destroyed when the
277                        // InCallActivity is about to finish.
278                        final String textMessage = et.getText().toString().trim();
279                        dismissCustomMessagePopup();
280                        getPresenter().rejectCallWithMessage(textMessage);
281                    }
282                })
283                .setNegativeButton(R.string.custom_message_cancel,
284                        new DialogInterface.OnClickListener() {
285                    @Override
286                    public void onClick(DialogInterface dialog, int which) {
287                        dismissCustomMessagePopup();
288                        getPresenter().onDismissDialog();
289                    }
290                })
291                .setTitle(R.string.respond_via_sms_custom_message);
292        mCustomMessagePopup = builder.create();
293
294        // Enable/disable the send button based on whether there is a message in the EditText
295        et.addTextChangedListener(new TextWatcher() {
296            @Override
297            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
298            }
299
300            @Override
301            public void onTextChanged(CharSequence s, int start, int before, int count) {
302            }
303
304            @Override
305            public void afterTextChanged(Editable s) {
306                final Button sendButton = mCustomMessagePopup.getButton(
307                        DialogInterface.BUTTON_POSITIVE);
308                sendButton.setEnabled(s != null && s.toString().trim().length() != 0);
309            }
310        });
311
312        // Keyboard up, show the dialog
313        mCustomMessagePopup.getWindow().setSoftInputMode(
314                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
315        mCustomMessagePopup.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
316        mCustomMessagePopup.show();
317
318        // Send button starts out disabled
319        final Button sendButton = mCustomMessagePopup.getButton(DialogInterface.BUTTON_POSITIVE);
320        sendButton.setEnabled(false);
321    }
322
323    @Override
324    public void configureMessageDialog(List<String> textResponses) {
325        mSmsResponses.clear();
326        mSmsResponses.addAll(textResponses);
327        mSmsResponses.add(getResources().getString(
328                R.string.respond_via_sms_custom_message));
329        if (mSmsResponsesAdapter != null) {
330            mSmsResponsesAdapter.notifyDataSetChanged();
331        }
332    }
333
334    @Override
335    public Context getContext() {
336        return getActivity();
337    }
338
339    @Override
340    public void onAnswer(int videoState, Context context) {
341        getPresenter().onAnswer(videoState, context);
342    }
343
344    @Override
345    public void onDecline() {
346        getPresenter().onDecline();
347    }
348
349    @Override
350    public void onText() {
351        getPresenter().onText();
352    }
353
354    /**
355     * OnItemClickListener for the "Respond via SMS" popup.
356     */
357    public class RespondViaSmsItemClickListener implements AdapterView.OnItemClickListener {
358
359        /**
360         * Handles the user selecting an item from the popup.
361         */
362        @Override
363        public void onItemClick(AdapterView<?> parent,  // The ListView
364                View view,  // The TextView that was clicked
365                int position, long id) {
366            Log.d(this, "RespondViaSmsItemClickListener.onItemClick(" + position + ")...");
367            final String message = (String) parent.getItemAtPosition(position);
368            Log.v(this, "- message: '" + message + "'");
369            dismissCannedResponsePopup();
370
371            // The "Custom" choice is a special case.
372            // (For now, it's guaranteed to be the last item.)
373            if (position == (parent.getCount() - 1)) {
374                // Show the custom message dialog
375                showCustomMessageDialog();
376            } else {
377                getPresenter().rejectCallWithMessage(message);
378            }
379        }
380    }
381}
382