Blanket copy of PhoneApp to services/Telephony.
First phase of splitting out InCallUI from PhoneApp.
Change-Id: I237341c4ff00e96c677caa4580b251ef3432931b
diff --git a/src/com/android/phone/SipCallOptionHandler.java b/src/com/android/phone/SipCallOptionHandler.java
new file mode 100644
index 0000000..500f322
--- /dev/null
+++ b/src/com/android/phone/SipCallOptionHandler.java
@@ -0,0 +1,448 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import com.android.internal.telephony.CallManager;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.phone.sip.SipProfileDb;
+import com.android.phone.sip.SipSettings;
+import com.android.phone.sip.SipSharedPreferences;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.net.sip.SipException;
+import android.net.sip.SipManager;
+import android.net.sip.SipProfile;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.PhoneNumberUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Activity that selects the proper phone type for an outgoing call.
+ *
+ * This activity determines which Phone type (SIP or PSTN) should be used
+ * for an outgoing phone call, depending on the outgoing "number" (which
+ * may be either a PSTN number or a SIP address) as well as the user's SIP
+ * preferences. In some cases this activity has no interaction with the
+ * user, but in other cases it may (by bringing up a dialog if the user's
+ * preference is "Ask for each call".)
+ */
+public class SipCallOptionHandler extends Activity implements
+ DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
+ CompoundButton.OnCheckedChangeListener {
+ static final String TAG = "SipCallOptionHandler";
+ private static final boolean DBG =
+ (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
+
+ static final int DIALOG_SELECT_PHONE_TYPE = 0;
+ static final int DIALOG_SELECT_OUTGOING_SIP_PHONE = 1;
+ static final int DIALOG_START_SIP_SETTINGS = 2;
+ static final int DIALOG_NO_INTERNET_ERROR = 3;
+ static final int DIALOG_NO_VOIP = 4;
+ static final int DIALOG_SIZE = 5;
+
+ private Intent mIntent;
+ private List<SipProfile> mProfileList;
+ private String mCallOption;
+ private String mNumber;
+ private SipSharedPreferences mSipSharedPreferences;
+ private SipProfileDb mSipProfileDb;
+ private Dialog[] mDialogs = new Dialog[DIALOG_SIZE];
+ private SipProfile mOutgoingSipProfile;
+ private TextView mUnsetPriamryHint;
+ private boolean mUseSipPhone = false;
+ private boolean mMakePrimary = false;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ String action = intent.getAction();
+
+ // This activity is only ever launched with the
+ // ACTION_SIP_SELECT_PHONE action.
+ if (!OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE.equals(action)) {
+ Log.wtf(TAG, "onCreate: got intent action '" + action + "', expected "
+ + OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE);
+ finish();
+ return;
+ }
+
+ // mIntent is a copy of the original CALL intent that started the
+ // whole outgoing-call sequence. This intent will ultimately be
+ // passed to CallController.placeCall() after displaying the SIP
+ // call options dialog (if necessary).
+ mIntent = (Intent) intent.getParcelableExtra(OutgoingCallBroadcaster.EXTRA_NEW_CALL_INTENT);
+ if (mIntent == null) {
+ finish();
+ return;
+ }
+
+ // Allow this activity to be visible in front of the keyguard.
+ // (This is only necessary for obscure scenarios like the user
+ // initiating a call and then immediately pressing the Power
+ // button.)
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+
+ // If we're trying to make a SIP call, return a SipPhone if one is
+ // available.
+ //
+ // - If it's a sip: URI, this is definitely a SIP call, regardless
+ // of whether the data is a SIP address or a regular phone
+ // number.
+ //
+ // - If this is a tel: URI but the data contains an "@" character
+ // (see PhoneNumberUtils.isUriNumber()) we consider that to be a
+ // SIP number too.
+ //
+ // TODO: Eventually we may want to disallow that latter case
+ // (e.g. "tel:foo@example.com").
+ //
+ // TODO: We should also consider moving this logic into the
+ // CallManager, where it could be made more generic.
+ // (For example, each "telephony provider" could be allowed
+ // to register the URI scheme(s) that it can handle, and the
+ // CallManager would then find the best match for every
+ // outgoing call.)
+
+ boolean voipSupported = PhoneUtils.isVoipSupported();
+ if (DBG) Log.v(TAG, "voipSupported: " + voipSupported);
+ mSipProfileDb = new SipProfileDb(this);
+ mSipSharedPreferences = new SipSharedPreferences(this);
+ mCallOption = mSipSharedPreferences.getSipCallOption();
+ if (DBG) Log.v(TAG, "Call option: " + mCallOption);
+ Uri uri = mIntent.getData();
+ String scheme = uri.getScheme();
+ mNumber = PhoneNumberUtils.getNumberFromIntent(mIntent, this);
+ boolean isInCellNetwork = PhoneGlobals.getInstance().phoneMgr.isRadioOn();
+ boolean isKnownCallScheme = Constants.SCHEME_TEL.equals(scheme)
+ || Constants.SCHEME_SIP.equals(scheme);
+ boolean isRegularCall = Constants.SCHEME_TEL.equals(scheme)
+ && !PhoneNumberUtils.isUriNumber(mNumber);
+
+ // Bypass the handler if the call scheme is not sip or tel.
+ if (!isKnownCallScheme) {
+ setResultAndFinish();
+ return;
+ }
+
+ // Check if VoIP feature is supported.
+ if (!voipSupported) {
+ if (!isRegularCall) {
+ showDialog(DIALOG_NO_VOIP);
+ } else {
+ setResultAndFinish();
+ }
+ return;
+ }
+
+ // Since we are not sure if anyone has touched the number during
+ // the NEW_OUTGOING_CALL broadcast, we just check if the provider
+ // put their gateway information in the intent. If so, it means
+ // someone has changed the destination number. We then make the
+ // call via the default pstn network. However, if one just alters
+ // the destination directly, then we still let it go through the
+ // Internet call option process.
+ if (!PhoneUtils.hasPhoneProviderExtras(mIntent)) {
+ if (!isNetworkConnected()) {
+ if (!isRegularCall) {
+ showDialog(DIALOG_NO_INTERNET_ERROR);
+ return;
+ }
+ } else {
+ if (mCallOption.equals(Settings.System.SIP_ASK_ME_EACH_TIME)
+ && isRegularCall && isInCellNetwork) {
+ showDialog(DIALOG_SELECT_PHONE_TYPE);
+ return;
+ }
+ if (!mCallOption.equals(Settings.System.SIP_ADDRESS_ONLY)
+ || !isRegularCall) {
+ mUseSipPhone = true;
+ }
+ }
+ }
+
+ if (mUseSipPhone) {
+ // If there is no sip profile and it is a regular call, then we
+ // should use pstn network instead.
+ if ((mSipProfileDb.getProfilesCount() > 0) || !isRegularCall) {
+ startGetPrimarySipPhoneThread();
+ return;
+ } else {
+ mUseSipPhone = false;
+ }
+ }
+ setResultAndFinish();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (isFinishing()) return;
+ for (Dialog dialog : mDialogs) {
+ if (dialog != null) dialog.dismiss();
+ }
+ finish();
+ }
+
+ protected Dialog onCreateDialog(int id) {
+ Dialog dialog;
+ switch(id) {
+ case DIALOG_SELECT_PHONE_TYPE:
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.pick_outgoing_call_phone_type)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setSingleChoiceItems(R.array.phone_type_values, -1, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setOnCancelListener(this)
+ .create();
+ break;
+ case DIALOG_SELECT_OUTGOING_SIP_PHONE:
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.pick_outgoing_sip_phone)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setSingleChoiceItems(getProfileNameArray(), -1, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setOnCancelListener(this)
+ .create();
+ addMakeDefaultCheckBox(dialog);
+ break;
+ case DIALOG_START_SIP_SETTINGS:
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.no_sip_account_found_title)
+ .setMessage(R.string.no_sip_account_found)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setPositiveButton(R.string.sip_menu_add, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setOnCancelListener(this)
+ .create();
+ break;
+ case DIALOG_NO_INTERNET_ERROR:
+ boolean wifiOnly = SipManager.isSipWifiOnly(this);
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(wifiOnly ? R.string.no_wifi_available_title
+ : R.string.no_internet_available_title)
+ .setMessage(wifiOnly ? R.string.no_wifi_available
+ : R.string.no_internet_available)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setPositiveButton(android.R.string.ok, this)
+ .setOnCancelListener(this)
+ .create();
+ break;
+ case DIALOG_NO_VOIP:
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.no_voip)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setPositiveButton(android.R.string.ok, this)
+ .setOnCancelListener(this)
+ .create();
+ break;
+ default:
+ dialog = null;
+ }
+ if (dialog != null) {
+ mDialogs[id] = dialog;
+ }
+ return dialog;
+ }
+
+ private void addMakeDefaultCheckBox(Dialog dialog) {
+ LayoutInflater inflater = (LayoutInflater) getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(
+ com.android.internal.R.layout.always_use_checkbox, null);
+ CheckBox makePrimaryCheckBox =
+ (CheckBox)view.findViewById(com.android.internal.R.id.alwaysUse);
+ makePrimaryCheckBox.setText(R.string.remember_my_choice);
+ makePrimaryCheckBox.setOnCheckedChangeListener(this);
+ mUnsetPriamryHint = (TextView)view.findViewById(
+ com.android.internal.R.id.clearDefaultHint);
+ mUnsetPriamryHint.setText(R.string.reset_my_choice_hint);
+ mUnsetPriamryHint.setVisibility(View.GONE);
+ ((AlertDialog)dialog).setView(view);
+ }
+
+ private CharSequence[] getProfileNameArray() {
+ CharSequence[] entries = new CharSequence[mProfileList.size()];
+ int i = 0;
+ for (SipProfile p : mProfileList) {
+ entries[i++] = p.getProfileName();
+ }
+ return entries;
+ }
+
+ public void onClick(DialogInterface dialog, int id) {
+ if (id == DialogInterface.BUTTON_NEGATIVE) {
+ // button negative is cancel
+ finish();
+ return;
+ } else if(dialog == mDialogs[DIALOG_SELECT_PHONE_TYPE]) {
+ String selection = getResources().getStringArray(
+ R.array.phone_type_values)[id];
+ if (DBG) Log.v(TAG, "User pick phone " + selection);
+ if (selection.equals(getString(R.string.internet_phone))) {
+ mUseSipPhone = true;
+ startGetPrimarySipPhoneThread();
+ return;
+ }
+ } else if (dialog == mDialogs[DIALOG_SELECT_OUTGOING_SIP_PHONE]) {
+ mOutgoingSipProfile = mProfileList.get(id);
+ } else if ((dialog == mDialogs[DIALOG_NO_INTERNET_ERROR])
+ || (dialog == mDialogs[DIALOG_NO_VOIP])) {
+ finish();
+ return;
+ } else {
+ if (id == DialogInterface.BUTTON_POSITIVE) {
+ // Redirect to sip settings and drop the call.
+ Intent newIntent = new Intent(this, SipSettings.class);
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(newIntent);
+ }
+ finish();
+ return;
+ }
+ setResultAndFinish();
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ mMakePrimary = isChecked;
+ if (isChecked) {
+ mUnsetPriamryHint.setVisibility(View.VISIBLE);
+ } else {
+ mUnsetPriamryHint.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private void createSipPhoneIfNeeded(SipProfile p) {
+ CallManager cm = PhoneGlobals.getInstance().mCM;
+ if (PhoneUtils.getSipPhoneFromUri(cm, p.getUriString()) != null) return;
+
+ // Create the phone since we can not find it in CallManager
+ try {
+ SipManager.newInstance(this).open(p);
+ Phone phone = PhoneFactory.makeSipPhone(p.getUriString());
+ if (phone != null) {
+ cm.registerPhone(phone);
+ } else {
+ Log.e(TAG, "cannot make sipphone profile" + p);
+ }
+ } catch (SipException e) {
+ Log.e(TAG, "cannot open sip profile" + p, e);
+ }
+ }
+
+ private void setResultAndFinish() {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ if (mOutgoingSipProfile != null) {
+ if (!isNetworkConnected()) {
+ showDialog(DIALOG_NO_INTERNET_ERROR);
+ return;
+ }
+ if (DBG) Log.v(TAG, "primary SIP URI is " +
+ mOutgoingSipProfile.getUriString());
+ createSipPhoneIfNeeded(mOutgoingSipProfile);
+ mIntent.putExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI,
+ mOutgoingSipProfile.getUriString());
+ if (mMakePrimary) {
+ mSipSharedPreferences.setPrimaryAccount(
+ mOutgoingSipProfile.getUriString());
+ }
+ }
+
+ if (mUseSipPhone && mOutgoingSipProfile == null) {
+ showDialog(DIALOG_START_SIP_SETTINGS);
+ return;
+ } else {
+ // Woo hoo -- it's finally OK to initiate the outgoing call!
+ PhoneGlobals.getInstance().callController.placeCall(mIntent);
+ }
+ finish();
+ }
+ });
+ }
+
+ private boolean isNetworkConnected() {
+ ConnectivityManager cm = (ConnectivityManager) getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ if (cm != null) {
+ NetworkInfo ni = cm.getActiveNetworkInfo();
+ if ((ni == null) || !ni.isConnected()) return false;
+
+ return ((ni.getType() == ConnectivityManager.TYPE_WIFI)
+ || !SipManager.isSipWifiOnly(this));
+ }
+ return false;
+ }
+
+ private void startGetPrimarySipPhoneThread() {
+ new Thread(new Runnable() {
+ public void run() {
+ getPrimarySipPhone();
+ }
+ }).start();
+ }
+
+ private void getPrimarySipPhone() {
+ String primarySipUri = mSipSharedPreferences.getPrimaryAccount();
+
+ mOutgoingSipProfile = getPrimaryFromExistingProfiles(primarySipUri);
+ if (mOutgoingSipProfile == null) {
+ if ((mProfileList != null) && (mProfileList.size() > 0)) {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ showDialog(DIALOG_SELECT_OUTGOING_SIP_PHONE);
+ }
+ });
+ return;
+ }
+ }
+ setResultAndFinish();
+ }
+
+ private SipProfile getPrimaryFromExistingProfiles(String primarySipUri) {
+ mProfileList = mSipProfileDb.retrieveSipProfileList();
+ if (mProfileList == null) return null;
+ for (SipProfile p : mProfileList) {
+ if (p.getUriString().equals(primarySipUri)) return p;
+ }
+ return null;
+ }
+}