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