Home | History | Annotate | Download | only in incallui
      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 
     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