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;
+    }
+}