Home | History | Annotate | Download | only in phone
      1 /**
      2  * Copyright (C) 2010 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.phone;
     18 
     19 import com.android.internal.telephony.CallManager;
     20 import com.android.internal.telephony.Phone;
     21 import com.android.internal.telephony.PhoneFactory;
     22 import com.android.phone.sip.SipProfileDb;
     23 import com.android.phone.sip.SipSettings;
     24 import com.android.phone.sip.SipSharedPreferences;
     25 
     26 import android.app.Activity;
     27 import android.app.AlertDialog;
     28 import android.app.Dialog;
     29 import android.content.Context;
     30 import android.content.DialogInterface;
     31 import android.content.Intent;
     32 import android.net.ConnectivityManager;
     33 import android.net.NetworkInfo;
     34 import android.net.sip.SipException;
     35 import android.net.sip.SipManager;
     36 import android.net.sip.SipProfile;
     37 import android.net.Uri;
     38 import android.os.Bundle;
     39 import android.provider.Settings;
     40 import android.telephony.PhoneNumberUtils;
     41 import android.util.Log;
     42 import android.view.LayoutInflater;
     43 import android.view.View;
     44 import android.view.WindowManager;
     45 import android.widget.CheckBox;
     46 import android.widget.CompoundButton;
     47 import android.widget.TextView;
     48 
     49 import java.util.List;
     50 
     51 /**
     52  * SipCallOptionHandler select the sip phone based on the call option.
     53  */
     54 public class SipCallOptionHandler extends Activity implements
     55         DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
     56         CompoundButton.OnCheckedChangeListener {
     57 
     58     static final String TAG = "SipCallOptionHandler";
     59     static final int DIALOG_SELECT_PHONE_TYPE = 0;
     60     static final int DIALOG_SELECT_OUTGOING_SIP_PHONE = 1;
     61     static final int DIALOG_START_SIP_SETTINGS = 2;
     62     static final int DIALOG_NO_INTERNET_ERROR = 3;
     63     static final int DIALOG_NO_VOIP = 4;
     64     static final int DIALOG_SIZE = 5;
     65 
     66     private Intent mIntent;
     67     private List<SipProfile> mProfileList;
     68     private String mCallOption;
     69     private String mNumber;
     70     private SipSharedPreferences mSipSharedPreferences;
     71     private SipProfileDb mSipProfileDb;
     72     private Dialog[] mDialogs = new Dialog[DIALOG_SIZE];
     73     private SipProfile mOutgoingSipProfile;
     74     private TextView mUnsetPriamryHint;
     75     private boolean mUseSipPhone = false;
     76     private boolean mMakePrimary = false;
     77 
     78     @Override
     79     public void onCreate(Bundle savedInstanceState) {
     80         super.onCreate(savedInstanceState);
     81 
     82         mIntent = (Intent)getIntent().getParcelableExtra
     83             (OutgoingCallBroadcaster.EXTRA_NEW_CALL_INTENT);
     84         if (mIntent == null) {
     85             finish();
     86             return;
     87         }
     88 
     89         // set this flag so this activity will stay in front of the keyguard
     90         getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
     91 
     92         // If we're trying to make a SIP call, return a SipPhone if one is
     93         // available.
     94         //
     95         // - If it's a sip: URI, this is definitely a SIP call, regardless
     96         //   of whether the data is a SIP address or a regular phone
     97         //   number.
     98         //
     99         // - If this is a tel: URI but the data contains an "@" character
    100         //   (see PhoneNumberUtils.isUriNumber()) we consider that to be a
    101         //   SIP number too.
    102         //
    103         // TODO: Eventually we may want to disallow that latter case
    104         //       (e.g. "tel:foo (at) example.com").
    105         //
    106         // TODO: We should also consider moving this logic into the
    107         //       CallManager, where it could be made more generic.
    108         //       (For example, each "telephony provider" could be allowed
    109         //       to register the URI scheme(s) that it can handle, and the
    110         //       CallManager would then find the best match for every
    111         //       outgoing call.)
    112 
    113         boolean voipSupported = SipManager.isVoipSupported(this);
    114         mSipProfileDb = new SipProfileDb(this);
    115         mSipSharedPreferences = new SipSharedPreferences(this);
    116         mCallOption = mSipSharedPreferences.getSipCallOption();
    117         Log.v(TAG, "Call option is " + mCallOption);
    118         Uri uri = mIntent.getData();
    119         String scheme = uri.getScheme();
    120         mNumber = PhoneNumberUtils.getNumberFromIntent(mIntent, this);
    121         boolean isInCellNetwork = PhoneApp.getInstance().phoneMgr.isRadioOn();
    122         boolean isKnownCallScheme= "tel".equals(scheme) || "sip".equals(scheme);
    123         boolean isRegularCall =
    124                 "tel".equals(scheme) && !PhoneNumberUtils.isUriNumber(mNumber);
    125 
    126         // Bypass the handler if the call scheme is not sip or tel.
    127         if (!isKnownCallScheme) {
    128             setResultAndFinish();
    129             return;
    130         }
    131 
    132         // Check if VoIP feature is supported.
    133         if (!voipSupported) {
    134             if (!isRegularCall) {
    135                 showDialog(DIALOG_NO_VOIP);
    136             } else {
    137                 setResultAndFinish();
    138             }
    139             return;
    140         }
    141 
    142         // Since we are not sure if anyone has touched the number during
    143         // the NEW_OUTGOING_CALL broadcast, we just check if the provider
    144         // put their gateway information in the intent. If so, it means
    145         // someone has changed the destination number. We then make the
    146         // call via the default pstn network. However, if one just alters
    147         // the destination directly, then we still let it go through the
    148         // Internet call option process.
    149         if (!PhoneUtils.hasPhoneProviderExtras(mIntent)) {
    150             if (!isNetworkConnected()) {
    151                 if (!isRegularCall) {
    152                     showDialog(DIALOG_NO_INTERNET_ERROR);
    153                     return;
    154                 }
    155             } else {
    156                 if (mCallOption.equals(Settings.System.SIP_ASK_ME_EACH_TIME)
    157                         && isRegularCall && isInCellNetwork) {
    158                     showDialog(DIALOG_SELECT_PHONE_TYPE);
    159                     return;
    160                 }
    161                 if (!mCallOption.equals(Settings.System.SIP_ADDRESS_ONLY)
    162                         || !isRegularCall) {
    163                     mUseSipPhone = true;
    164                 }
    165             }
    166         }
    167 
    168         if (mUseSipPhone) {
    169             // If there is no sip profile and it is a regular call, then we
    170             // should use pstn network instead.
    171             if ((mSipProfileDb.getProfilesCount() > 0) || !isRegularCall) {
    172                 startGetPrimarySipPhoneThread();
    173                 return;
    174             } else {
    175                 mUseSipPhone = false;
    176             }
    177         }
    178         setResultAndFinish();
    179     }
    180 
    181     @Override
    182     public void onPause() {
    183         super.onPause();
    184         if (isFinishing()) return;
    185         for (Dialog dialog : mDialogs) {
    186             if (dialog != null) dialog.dismiss();
    187         }
    188         finish();
    189     }
    190 
    191     protected Dialog onCreateDialog(int id) {
    192         Dialog dialog;
    193         switch(id) {
    194         case DIALOG_SELECT_PHONE_TYPE:
    195             dialog = new AlertDialog.Builder(this)
    196                     .setTitle(R.string.pick_outgoing_call_phone_type)
    197                     .setIcon(android.R.drawable.ic_dialog_alert)
    198                     .setSingleChoiceItems(R.array.phone_type_values, -1, this)
    199                     .setNegativeButton(android.R.string.cancel, this)
    200                     .setOnCancelListener(this)
    201                     .create();
    202             break;
    203         case DIALOG_SELECT_OUTGOING_SIP_PHONE:
    204             dialog = new AlertDialog.Builder(this)
    205                     .setTitle(R.string.pick_outgoing_sip_phone)
    206                     .setIcon(android.R.drawable.ic_dialog_alert)
    207                     .setSingleChoiceItems(getProfileNameArray(), -1, this)
    208                     .setNegativeButton(android.R.string.cancel, this)
    209                     .setOnCancelListener(this)
    210                     .create();
    211             addMakeDefaultCheckBox(dialog);
    212             break;
    213         case DIALOG_START_SIP_SETTINGS:
    214             dialog = new AlertDialog.Builder(this)
    215                     .setTitle(R.string.no_sip_account_found_title)
    216                     .setMessage(R.string.no_sip_account_found)
    217                     .setIcon(android.R.drawable.ic_dialog_alert)
    218                     .setPositiveButton(R.string.sip_menu_add, this)
    219                     .setNegativeButton(android.R.string.cancel, this)
    220                     .setOnCancelListener(this)
    221                     .create();
    222             break;
    223         case DIALOG_NO_INTERNET_ERROR:
    224             boolean wifiOnly = SipManager.isSipWifiOnly(this);
    225             dialog = new AlertDialog.Builder(this)
    226                     .setTitle(wifiOnly ? R.string.no_wifi_available_title
    227                                        : R.string.no_internet_available_title)
    228                     .setMessage(wifiOnly ? R.string.no_wifi_available
    229                                          : R.string.no_internet_available)
    230                     .setIcon(android.R.drawable.ic_dialog_alert)
    231                     .setPositiveButton(android.R.string.ok, this)
    232                     .setOnCancelListener(this)
    233                     .create();
    234             break;
    235         case DIALOG_NO_VOIP:
    236             dialog = new AlertDialog.Builder(this)
    237                     .setTitle(R.string.no_voip)
    238                     .setIcon(android.R.drawable.ic_dialog_alert)
    239                     .setPositiveButton(android.R.string.ok, this)
    240                     .setOnCancelListener(this)
    241                     .create();
    242             break;
    243         default:
    244             dialog = null;
    245         }
    246         if (dialog != null) {
    247             mDialogs[id] = dialog;
    248         }
    249         return dialog;
    250     }
    251 
    252     private void addMakeDefaultCheckBox(Dialog dialog) {
    253         LayoutInflater inflater = (LayoutInflater) getSystemService(
    254                 Context.LAYOUT_INFLATER_SERVICE);
    255         View view = inflater.inflate(
    256                 com.android.internal.R.layout.always_use_checkbox, null);
    257         CheckBox makePrimaryCheckBox =
    258                 (CheckBox)view.findViewById(com.android.internal.R.id.alwaysUse);
    259         makePrimaryCheckBox.setText(R.string.remember_my_choice);
    260         makePrimaryCheckBox.setOnCheckedChangeListener(this);
    261         mUnsetPriamryHint = (TextView)view.findViewById(
    262                 com.android.internal.R.id.clearDefaultHint);
    263         mUnsetPriamryHint.setText(R.string.reset_my_choice_hint);
    264         mUnsetPriamryHint.setVisibility(View.GONE);
    265         ((AlertDialog)dialog).setView(view);
    266     }
    267 
    268     private CharSequence[] getProfileNameArray() {
    269         CharSequence[] entries = new CharSequence[mProfileList.size()];
    270         int i = 0;
    271         for (SipProfile p : mProfileList) {
    272             entries[i++] = p.getProfileName();
    273         }
    274         return entries;
    275     }
    276 
    277     public void onClick(DialogInterface dialog, int id) {
    278         if (id == DialogInterface.BUTTON_NEGATIVE) {
    279             // button negative is cancel
    280             finish();
    281             return;
    282         } else if(dialog == mDialogs[DIALOG_SELECT_PHONE_TYPE]) {
    283             String selection = getResources().getStringArray(
    284                     R.array.phone_type_values)[id];
    285             Log.v(TAG, "User pick phone " + selection);
    286             if (selection.equals(getString(R.string.internet_phone))) {
    287                 mUseSipPhone = true;
    288                 startGetPrimarySipPhoneThread();
    289                 return;
    290             }
    291         } else if (dialog == mDialogs[DIALOG_SELECT_OUTGOING_SIP_PHONE]) {
    292             mOutgoingSipProfile = mProfileList.get(id);
    293         } else if ((dialog == mDialogs[DIALOG_NO_INTERNET_ERROR])
    294                 || (dialog == mDialogs[DIALOG_NO_VOIP])) {
    295             finish();
    296             return;
    297         } else {
    298             if (id == DialogInterface.BUTTON_POSITIVE) {
    299                 // Redirect to sip settings and drop the call.
    300                 Intent newIntent = new Intent(this, SipSettings.class);
    301                 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    302                 startActivity(newIntent);
    303             }
    304             finish();
    305             return;
    306         }
    307         setResultAndFinish();
    308     }
    309 
    310     public void onCancel(DialogInterface dialog) {
    311         finish();
    312     }
    313 
    314     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    315         mMakePrimary = isChecked;
    316         if (isChecked) {
    317             mUnsetPriamryHint.setVisibility(View.VISIBLE);
    318         } else {
    319             mUnsetPriamryHint.setVisibility(View.INVISIBLE);
    320         }
    321     }
    322 
    323     private void createSipPhoneIfNeeded(SipProfile p) {
    324         CallManager cm = PhoneApp.getInstance().mCM;
    325         if (PhoneUtils.getSipPhoneFromUri(cm, p.getUriString()) != null) return;
    326 
    327         // Create the phone since we can not find it in CallManager
    328         try {
    329             SipManager.newInstance(this).open(p);
    330             Phone phone = PhoneFactory.makeSipPhone(p.getUriString());
    331             if (phone != null) {
    332                 cm.registerPhone(phone);
    333             } else {
    334                 Log.e(TAG, "cannot make sipphone profile" + p);
    335             }
    336         } catch (SipException e) {
    337             Log.e(TAG, "cannot open sip profile" + p, e);
    338         }
    339     }
    340 
    341     private void setResultAndFinish() {
    342         runOnUiThread(new Runnable() {
    343             public void run() {
    344                 if (mOutgoingSipProfile != null) {
    345                     if (!isNetworkConnected()) {
    346                         showDialog(DIALOG_NO_INTERNET_ERROR);
    347                         return;
    348                     }
    349                     Log.v(TAG, "primary SIP URI is " +
    350                             mOutgoingSipProfile.getUriString());
    351                     createSipPhoneIfNeeded(mOutgoingSipProfile);
    352                     mIntent.putExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI,
    353                             mOutgoingSipProfile.getUriString());
    354                     if (mMakePrimary) {
    355                         mSipSharedPreferences.setPrimaryAccount(
    356                                 mOutgoingSipProfile.getUriString());
    357                     }
    358                 }
    359                 if (mUseSipPhone && mOutgoingSipProfile == null) {
    360                     showDialog(DIALOG_START_SIP_SETTINGS);
    361                     return;
    362                 } else {
    363                     startActivity(mIntent);
    364                 }
    365                 finish();
    366             }
    367         });
    368     }
    369 
    370     private boolean isNetworkConnected() {
    371         ConnectivityManager cm = (ConnectivityManager) getSystemService(
    372                 Context.CONNECTIVITY_SERVICE);
    373         if (cm != null) {
    374             NetworkInfo ni = cm.getActiveNetworkInfo();
    375             if ((ni == null) || !ni.isConnected()) return false;
    376 
    377             return ((ni.getType() == ConnectivityManager.TYPE_WIFI)
    378                     || !SipManager.isSipWifiOnly(this));
    379         }
    380         return false;
    381     }
    382 
    383     private void startGetPrimarySipPhoneThread() {
    384         new Thread(new Runnable() {
    385             public void run() {
    386                 getPrimarySipPhone();
    387             }
    388         }).start();
    389     }
    390 
    391     private void getPrimarySipPhone() {
    392         String primarySipUri = mSipSharedPreferences.getPrimaryAccount();
    393 
    394         mOutgoingSipProfile = getPrimaryFromExistingProfiles(primarySipUri);
    395         if (mOutgoingSipProfile == null) {
    396             if ((mProfileList != null) && (mProfileList.size() > 0)) {
    397                 runOnUiThread(new Runnable() {
    398                     public void run() {
    399                         showDialog(DIALOG_SELECT_OUTGOING_SIP_PHONE);
    400                     }
    401                 });
    402                 return;
    403             }
    404         }
    405         setResultAndFinish();
    406     }
    407 
    408     private SipProfile getPrimaryFromExistingProfiles(String primarySipUri) {
    409         mProfileList = mSipProfileDb.retrieveSipProfileList();
    410         if (mProfileList == null) return null;
    411         for (SipProfile p : mProfileList) {
    412             if (p.getUriString().equals(primarySipUri)) return p;
    413         }
    414         return null;
    415     }
    416 }
    417