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