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