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 
17 package com.android.incallui;
18 
19 import android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.os.Bundle;
24 import android.text.Editable;
25 import android.text.TextWatcher;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.view.WindowManager;
30 import android.widget.AdapterView;
31 import android.widget.ArrayAdapter;
32 import android.widget.Button;
33 import android.widget.EditText;
34 import android.widget.ListView;
35 
36 import com.google.common.base.Preconditions;
37 import com.google.common.collect.Lists;
38 
39 import java.util.ArrayList;
40 import java.util.List;
41 
42 /**
43  *
44  */
45 public 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 
AnswerFragment()74     public AnswerFragment() {
75     }
76 
77     @Override
createPresenter()78     public AnswerPresenter createPresenter() {
79         return new AnswerPresenter();
80     }
81 
82     @Override
getUi()83     AnswerPresenter.AnswerUi getUi() {
84         return this;
85     }
86 
87     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onDestroyView()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
showAnswerUi(boolean show)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
showTargets(int targetSet)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
showMessageDialog()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 
isCannedResponsePopupShowing()213     private boolean isCannedResponsePopupShowing() {
214         if (mCannedResponsePopup != null) {
215             return mCannedResponsePopup.isShowing();
216         }
217         return false;
218     }
219 
isCustomMessagePopupShowing()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      */
dismissCannedResponsePopup()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      */
dismissCustomMessagePopup()243     private void dismissCustomMessagePopup() {
244        if (mCustomMessagePopup != null) {
245            mCustomMessagePopup.dismiss();
246            mCustomMessagePopup = null;
247        }
248     }
249 
dismissPendingDialogues()250     public void dismissPendingDialogues() {
251         if (isCannedResponsePopupShowing()) {
252             dismissCannedResponsePopup();
253         }
254 
255         if (isCustomMessagePopupShowing()) {
256             dismissCustomMessagePopup();
257         }
258     }
259 
hasPendingDialogs()260     public boolean hasPendingDialogs() {
261         return !(mCannedResponsePopup == null && mCustomMessagePopup == null);
262     }
263 
264     /**
265      * Shows the custom message entry dialog.
266      */
showCustomMessageDialog()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
configureMessageDialog(List<String> textResponses)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
getContext()335     public Context getContext() {
336         return getActivity();
337     }
338 
339     @Override
onAnswer(int videoState, Context context)340     public void onAnswer(int videoState, Context context) {
341         getPresenter().onAnswer(videoState, context);
342     }
343 
344     @Override
onDecline()345     public void onDecline() {
346         getPresenter().onDecline();
347     }
348 
349     @Override
onText()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
onItemClick(AdapterView<?> parent, View view, int position, long id)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