blob: d58386c113f4837a33315bb202e70d18b6d7497a [file] [log] [blame]
/*
* 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.sip;
import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.phone.CallFeaturesSetting;
import com.android.phone.R;
import com.android.phone.SipUtil;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.sip.SipErrorCode;
import android.net.sip.SipException;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
import android.net.sip.SipRegistrationListener;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* The PreferenceActivity class for managing sip profile preferences.
*/
public class SipSettings extends PreferenceActivity {
public static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
private static final int MENU_ADD_ACCOUNT = Menu.FIRST;
static final String KEY_SIP_PROFILE = "sip_profile";
private static final String BUTTON_SIP_RECEIVE_CALLS =
"sip_receive_calls_key";
private static final String PREF_SIP_LIST = "sip_account_list";
private static final String TAG = "SipSettings";
private static final int REQUEST_ADD_OR_EDIT_SIP_PROFILE = 1;
private PackageManager mPackageManager;
private SipManager mSipManager;
private CallManager mCallManager;
private SipProfileDb mProfileDb;
private SipProfile mProfile; // profile that's being edited
private CheckBoxPreference mButtonSipReceiveCalls;
private PreferenceCategory mSipListContainer;
private Map<String, SipPreference> mSipPreferenceMap;
private List<SipProfile> mSipProfileList;
private SipSharedPreferences mSipSharedPreferences;
private int mUid = Process.myUid();
private class SipPreference extends Preference {
SipProfile mProfile;
SipPreference(Context c, SipProfile p) {
super(c);
setProfile(p);
}
SipProfile getProfile() {
return mProfile;
}
void setProfile(SipProfile p) {
mProfile = p;
setTitle(getProfileName(p));
updateSummary(mSipSharedPreferences.isReceivingCallsEnabled()
? getString(R.string.registration_status_checking_status)
: getString(R.string.registration_status_not_receiving));
}
void updateSummary(String registrationStatus) {
int profileUid = mProfile.getCallingUid();
boolean isPrimary = mProfile.getUriString().equals(
mSipSharedPreferences.getPrimaryAccount());
Log.v(TAG, "profile uid is " + profileUid + " isPrimary:"
+ isPrimary + " registration:" + registrationStatus
+ " Primary:" + mSipSharedPreferences.getPrimaryAccount()
+ " status:" + registrationStatus);
String summary = "";
if ((profileUid > 0) && (profileUid != mUid)) {
// from third party apps
summary = getString(R.string.third_party_account_summary,
getPackageNameFromUid(profileUid));
} else if (isPrimary) {
summary = getString(R.string.primary_account_summary_with,
registrationStatus);
} else {
summary = registrationStatus;
}
setSummary(summary);
}
}
private String getPackageNameFromUid(int uid) {
try {
String[] pkgs = mPackageManager.getPackagesForUid(uid);
ApplicationInfo ai =
mPackageManager.getApplicationInfo(pkgs[0], 0);
return ai.loadLabel(mPackageManager).toString();
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "cannot find name of uid " + uid, e);
}
return "uid:" + uid;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSipManager = SipManager.newInstance(this);
mSipSharedPreferences = new SipSharedPreferences(this);
mProfileDb = new SipProfileDb(this);
mPackageManager = getPackageManager();
setContentView(R.layout.sip_settings_ui);
addPreferencesFromResource(R.xml.sip_setting);
mSipListContainer = (PreferenceCategory) findPreference(PREF_SIP_LIST);
registerForReceiveCallsCheckBox();
mCallManager = CallManager.getInstance();
updateProfilesStatus();
ActionBar actionBar = getActionBar();
if (actionBar != null) {
// android.R.id.home will be triggered in onOptionsItemSelected()
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
@Override
public void onResume() {
super.onResume();
if (mCallManager.getState() != PhoneConstants.State.IDLE) {
mButtonSipReceiveCalls.setEnabled(false);
} else {
mButtonSipReceiveCalls.setEnabled(true);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterForContextMenu(getListView());
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode,
final Intent intent) {
if (resultCode != RESULT_OK && resultCode != RESULT_FIRST_USER) return;
new Thread() {
@Override
public void run() {
try {
if (mProfile != null) {
Log.v(TAG, "Removed Profile:" + mProfile.getProfileName());
deleteProfile(mProfile);
}
SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE);
if (resultCode == RESULT_OK) {
Log.v(TAG, "New Profile Name:" + profile.getProfileName());
addProfile(profile);
}
updateProfilesStatus();
} catch (IOException e) {
Log.v(TAG, "Can not handle the profile : " + e.getMessage());
}
}
}.start();
}
private void registerForReceiveCallsCheckBox() {
mButtonSipReceiveCalls = (CheckBoxPreference) findPreference
(BUTTON_SIP_RECEIVE_CALLS);
mButtonSipReceiveCalls.setChecked(
mSipSharedPreferences.isReceivingCallsEnabled());
mButtonSipReceiveCalls.setOnPreferenceClickListener(
new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
final boolean enabled =
((CheckBoxPreference) preference).isChecked();
new Thread(new Runnable() {
public void run() {
handleSipReceiveCallsOption(enabled);
}
}).start();
return true;
}
});
}
private synchronized void handleSipReceiveCallsOption(boolean enabled) {
mSipSharedPreferences.setReceivingCallsEnabled(enabled);
List<SipProfile> sipProfileList = mProfileDb.retrieveSipProfileList();
for (SipProfile p : sipProfileList) {
String sipUri = p.getUriString();
p = updateAutoRegistrationFlag(p, enabled);
try {
if (enabled) {
mSipManager.open(p,
SipUtil.createIncomingCallPendingIntent(), null);
} else {
mSipManager.close(sipUri);
if (mSipSharedPreferences.isPrimaryAccount(sipUri)) {
// re-open in order to make calls
mSipManager.open(p);
}
}
} catch (Exception e) {
Log.e(TAG, "register failed", e);
}
}
updateProfilesStatus();
}
private SipProfile updateAutoRegistrationFlag(
SipProfile p, boolean enabled) {
SipProfile newProfile = new SipProfile.Builder(p)
.setAutoRegistration(enabled)
.build();
try {
mProfileDb.deleteProfile(p);
mProfileDb.saveProfile(newProfile);
} catch (Exception e) {
Log.e(TAG, "updateAutoRegistrationFlag error", e);
}
return newProfile;
}
private void updateProfilesStatus() {
new Thread(new Runnable() {
@Override
public void run() {
try {
retrieveSipLists();
} catch (Exception e) {
Log.e(TAG, "isRegistered", e);
}
}
}).start();
}
private String getProfileName(SipProfile profile) {
String profileName = profile.getProfileName();
if (TextUtils.isEmpty(profileName)) {
profileName = profile.getUserName() + "@" + profile.getSipDomain();
}
return profileName;
}
private void retrieveSipLists() {
mSipPreferenceMap = new LinkedHashMap<String, SipPreference>();
mSipProfileList = mProfileDb.retrieveSipProfileList();
processActiveProfilesFromSipService();
Collections.sort(mSipProfileList, new Comparator<SipProfile>() {
@Override
public int compare(SipProfile p1, SipProfile p2) {
return getProfileName(p1).compareTo(getProfileName(p2));
}
public boolean equals(SipProfile p) {
// not used
return false;
}
});
mSipListContainer.removeAll();
for (SipProfile p : mSipProfileList) {
addPreferenceFor(p);
}
if (!mSipSharedPreferences.isReceivingCallsEnabled()) return;
for (SipProfile p : mSipProfileList) {
if (mUid == p.getCallingUid()) {
try {
mSipManager.setRegistrationListener(
p.getUriString(), createRegistrationListener());
} catch (SipException e) {
Log.e(TAG, "cannot set registration listener", e);
}
}
}
}
private void processActiveProfilesFromSipService() {
SipProfile[] activeList = mSipManager.getListOfProfiles();
for (SipProfile activeProfile : activeList) {
SipProfile profile = getProfileFromList(activeProfile);
if (profile == null) {
mSipProfileList.add(activeProfile);
} else {
profile.setCallingUid(activeProfile.getCallingUid());
}
}
}
private SipProfile getProfileFromList(SipProfile activeProfile) {
for (SipProfile p : mSipProfileList) {
if (p.getUriString().equals(activeProfile.getUriString())) {
return p;
}
}
return null;
}
private void addPreferenceFor(SipProfile p) {
String status;
Log.v(TAG, "addPreferenceFor profile uri" + p.getUri());
SipPreference pref = new SipPreference(this, p);
mSipPreferenceMap.put(p.getUriString(), pref);
mSipListContainer.addPreference(pref);
pref.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference pref) {
handleProfileClick(((SipPreference) pref).mProfile);
return true;
}
});
}
private void handleProfileClick(final SipProfile profile) {
int uid = profile.getCallingUid();
if (uid == mUid || uid == 0) {
startSipEditor(profile);
return;
}
new AlertDialog.Builder(this)
.setTitle(R.string.alert_dialog_close)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setPositiveButton(R.string.close_profile,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int w) {
deleteProfile(profile);
unregisterProfile(profile);
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void unregisterProfile(final SipProfile p) {
// run it on background thread for better UI response
new Thread(new Runnable() {
@Override
public void run() {
try {
mSipManager.close(p.getUriString());
} catch (Exception e) {
Log.e(TAG, "unregister failed, SipService died?", e);
}
}
}, "unregisterProfile").start();
}
void deleteProfile(SipProfile p) {
mSipProfileList.remove(p);
SipPreference pref = mSipPreferenceMap.remove(p.getUriString());
mSipListContainer.removePreference(pref);
}
private void addProfile(SipProfile p) throws IOException {
try {
mSipManager.setRegistrationListener(p.getUriString(),
createRegistrationListener());
} catch (Exception e) {
Log.e(TAG, "cannot set registration listener", e);
}
mSipProfileList.add(p);
addPreferenceFor(p);
}
private void startSipEditor(final SipProfile profile) {
mProfile = profile;
Intent intent = new Intent(this, SipEditor.class);
intent.putExtra(KEY_SIP_PROFILE, (Parcelable) profile);
startActivityForResult(intent, REQUEST_ADD_OR_EDIT_SIP_PROFILE);
}
private void showRegistrationMessage(final String profileUri,
final String message) {
runOnUiThread(new Runnable() {
@Override
public void run() {
SipPreference pref = mSipPreferenceMap.get(profileUri);
if (pref != null) {
pref.updateSummary(message);
}
}
});
}
private SipRegistrationListener createRegistrationListener() {
return new SipRegistrationListener() {
@Override
public void onRegistrationDone(String profileUri, long expiryTime) {
showRegistrationMessage(profileUri, getString(
R.string.registration_status_done));
}
@Override
public void onRegistering(String profileUri) {
showRegistrationMessage(profileUri, getString(
R.string.registration_status_registering));
}
@Override
public void onRegistrationFailed(String profileUri, int errorCode,
String message) {
switch (errorCode) {
case SipErrorCode.IN_PROGRESS:
showRegistrationMessage(profileUri, getString(
R.string.registration_status_still_trying));
break;
case SipErrorCode.INVALID_CREDENTIALS:
showRegistrationMessage(profileUri, getString(
R.string.registration_status_invalid_credentials));
break;
case SipErrorCode.SERVER_UNREACHABLE:
showRegistrationMessage(profileUri, getString(
R.string.registration_status_server_unreachable));
break;
case SipErrorCode.DATA_CONNECTION_LOST:
if (SipManager.isSipWifiOnly(getApplicationContext())){
showRegistrationMessage(profileUri, getString(
R.string.registration_status_no_wifi_data));
} else {
showRegistrationMessage(profileUri, getString(
R.string.registration_status_no_data));
}
break;
case SipErrorCode.CLIENT_ERROR:
showRegistrationMessage(profileUri, getString(
R.string.registration_status_not_running));
break;
default:
showRegistrationMessage(profileUri, getString(
R.string.registration_status_failed_try_later,
message));
}
}
};
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_ADD_ACCOUNT, 0, R.string.add_sip_account)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(MENU_ADD_ACCOUNT).setEnabled(
mCallManager.getState() == PhoneConstants.State.IDLE);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
final int itemId = item.getItemId();
switch (itemId) {
case android.R.id.home: {
CallFeaturesSetting.goUpToTopLevelSetting(this);
return true;
}
case MENU_ADD_ACCOUNT: {
startSipEditor(null);
return true;
}
}
return super.onOptionsItemSelected(item);
}
}