diff options
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/incallui/AnswerPresenter.java | 138 | ||||
-rw-r--r-- | src/com/android/incallui/Call.java | 45 | ||||
-rw-r--r-- | src/com/android/incallui/CallButtonPresenter.java | 14 | ||||
-rw-r--r-- | src/com/android/incallui/CallList.java | 208 | ||||
-rw-r--r-- | src/com/android/incallui/ConferenceManagerFragment.java | 9 | ||||
-rw-r--r-- | src/com/android/incallui/InCallActivity.java | 147 | ||||
-rw-r--r-- | src/com/android/incallui/InCallPresenter.java | 35 | ||||
-rw-r--r-- | src/com/android/incallui/InCallServiceImpl.java | 14 | ||||
-rw-r--r-- | src/com/android/incallui/StatusBarNotifier.java | 10 | ||||
-rw-r--r-- | src/com/android/incallui/TelecomAdapter.java | 8 |
10 files changed, 574 insertions, 54 deletions
diff --git a/src/com/android/incallui/AnswerPresenter.java b/src/com/android/incallui/AnswerPresenter.java index 1468637c..505d2139 100644 --- a/src/com/android/incallui/AnswerPresenter.java +++ b/src/com/android/incallui/AnswerPresenter.java @@ -37,50 +37,69 @@ import java.util.List; public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> implements CallList.CallUpdateListener, InCallPresenter.InCallUiListener, InCallPresenter.IncomingCallListener, - CallList.Listener { + CallList.Listener, CallList.ActiveSubChangeListener { private static final String TAG = AnswerPresenter.class.getSimpleName(); - private String mCallId; - private Call mCall = null; + private String mCallId[] = new String[InCallServiceImpl.sPhoneCount]; + private Call mCall[] = new Call[InCallServiceImpl.sPhoneCount]; + private final CallList mCalls = CallList.getInstance(); private boolean mHasTextMessages = false; @Override public void onUiShowing(boolean showing) { if (showing) { - CallList.getInstance().addListener(this); - final CallList calls = CallList.getInstance(); + mCalls.addListener(this); + mCalls.addActiveSubChangeListener(this); Call call; - call = calls.getIncomingCall(); - if (call != null) { - processIncomingCall(call); + // Consider incoming/waiting calls on both subscriptions + // for DSDA. + for (int i = 0; i < InCallServiceImpl.sPhoneCount; i++) { + int[] subId = mCalls.getSubId(i); + call = mCalls.getCallWithState(Call.State.INCOMING, 0, subId[0]); + if (call == null) { + call = mCalls.getCallWithState(Call.State.CALL_WAITING, 0, subId[0]); + } + if (call != null) { + processIncomingCall(call); + } } - call = calls.getVideoUpgradeRequestCall(); + call = mCalls.getVideoUpgradeRequestCall(); Log.d(this, "getVideoUpgradeRequestCall call =" + call); if (call != null) { processVideoUpgradeRequestCall(call); } } else { - CallList.getInstance().removeListener(this); + mCalls.removeListener(this); // This is necessary because the activity can be destroyed while an incoming call exists. // This happens when back button is pressed while incoming call is still being shown. - if (mCallId != null) { - CallList.getInstance().removeCallUpdateListener(mCallId, this); + for (int i = 0; i < InCallServiceImpl.sPhoneCount; i++) { + int[] subId = mCalls.getSubId(i); + Call call = mCalls.getCallWithState(Call.State.INCOMING, 0, subId[0]); + if (call == null) { + call = mCalls.getCallWithState(Call.State.CALL_WAITING, 0, subId[0]); + } + if (mCallId[i] != null && call == null) { + mCalls.removeCallUpdateListener(mCallId[i], this); + mCalls.removeActiveSubChangeListener(this); + } } } } @Override public void onIncomingCall(InCallState oldState, InCallState newState, Call call) { + int subId = call.getSubId(); + int phoneId = mCalls.getPhoneId(subId); Log.d(this, "onIncomingCall: " + this); - Call modifyCall = CallList.getInstance().getVideoUpgradeRequestCall(); + Call modifyCall = mCalls.getVideoUpgradeRequestCall(); if (modifyCall != null) { showAnswerUi(false); Log.d(this, "declining upgrade request id: "); - CallList.getInstance().removeCallUpdateListener(mCallId, this); + mCalls.removeCallUpdateListener(mCallId[phoneId], this); InCallPresenter.getInstance().declineUpgradeRequest(getUi().getContext()); } - if (!call.getId().equals(mCallId)) { + if (!call.getId().equals(mCallId[phoneId])) { // A new call is coming in. processIncomingCall(call); } @@ -105,7 +124,11 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> if (!isUpgradePending) { // Stop listening for updates. - CallList.getInstance().removeCallUpdateListener(mCallId, this); + for (int i = 0; i < InCallServiceImpl.sPhoneCount; i++) { + if (mCallId[i] != null) { + mCalls.removeCallUpdateListener(mCallId[i], this); + } + } showAnswerUi(false); } } @@ -133,15 +156,17 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> } private void processIncomingCall(Call call) { - mCallId = call.getId(); - mCall = call; + int subId = call.getSubId(); + int phoneId = mCalls.getPhoneId(subId); + mCallId[phoneId] = call.getId(); + mCall[phoneId] = call; // Listen for call updates for the current call. - CallList.getInstance().addCallUpdateListener(mCallId, this); + mCalls.addCallUpdateListener(mCallId[phoneId], this); - Log.d(TAG, "Showing incoming for call id: " + mCallId + " " + this); + Log.d(TAG, "Showing incoming for call id: " + mCallId[phoneId] + " " + this); if (showAnswerUi(true)) { - final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId()); + final List<String> textMsgs = mCalls.getTextResponses(call.getId()); configureAnswerTargetsForSms(call, textMsgs); } } @@ -161,11 +186,13 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> private void processVideoUpgradeRequestCall(Call call) { Log.d(this, " processVideoUpgradeRequestCall call=" + call); - mCallId = call.getId(); - mCall = call; + int subId = call.getSubId(); + int phoneId = mCalls.getPhoneId(subId); + mCallId[phoneId] = call.getId(); + mCall[phoneId] = call; // Listen for call updates for the current call. - CallList.getInstance().addCallUpdateListener(mCallId, this); + CallList.getInstance().addCallUpdateListener(mCallId[phoneId], this); final int currentVideoState = call.getVideoState(); final int modifyToVideoState = call.getModifyToVideoState(); @@ -196,12 +223,14 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> Log.d(this, "onCallStateChange() " + call + " " + this); if (call.getState() != Call.State.INCOMING) { boolean isUpgradePending = isVideoUpgradePending(call); + int subId = call.getSubId(); + int phoneId = mCalls.getPhoneId(subId); if (!isUpgradePending) { // Stop listening for updates. - CallList.getInstance().removeCallUpdateListener(mCallId, this); + mCalls.removeCallUpdateListener(mCallId[phoneId], this); } - final Call incall = CallList.getInstance().getIncomingCall(); + final Call incall = mCalls.getIncomingCall(); if (incall != null || isUpgradePending) { showAnswerUi(true); } else { @@ -210,25 +239,43 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> mHasTextMessages = false; } else if (!mHasTextMessages) { - final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId()); + final List<String> textMsgs = mCalls.getTextResponses(call.getId()); if (textMsgs != null) { configureAnswerTargetsForSms(call, textMsgs); } } } + // get active phoneId, for which call is visible to user + private int getActivePhoneId() { + int phoneId = -1; + if (InCallServiceImpl.isDsdaEnabled()) { + int subId = mCalls.getActiveSubId(); + phoneId = mCalls.getPhoneId(subId); + } else { + for (int i = 0; i < mCall.length; i++) { + if (mCall[i] != null) { + phoneId = i; + } + } + } + return phoneId; + } + public void onAnswer(int videoState, Context context) { - if (mCallId == null) { + int phoneId = getActivePhoneId(); + Log.i(this, "onAnswer mCallId:" + mCallId + "phoneId:" + phoneId); + if (mCallId == null || phoneId == -1) { return; } - if (mCall.getSessionModificationState() + if (mCall[phoneId].getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState); InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context); } else { Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState); - TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState); + TelecomAdapter.getInstance().answerCall(mCall[phoneId].getId(), videoState); } } @@ -237,12 +284,13 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> * reject since it seems to be more prevalent. */ public void onDecline(Context context) { - Log.d(this, "onDecline " + mCallId); - if (mCall.getSessionModificationState() + int phoneId = getActivePhoneId(); + Log.d(this, "onDecline mCallId:" + mCallId + "phoneId:" + phoneId); + if (mCall[phoneId].getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { InCallPresenter.getInstance().declineUpgradeRequest(context); } else { - TelecomAdapter.getInstance().rejectCall(mCall.getId(), false, null); + TelecomAdapter.getInstance().rejectCall(mCall[phoneId].getId(), false, null); } } @@ -254,8 +302,9 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> } public void rejectCallWithMessage(String message) { - Log.d(this, "sendTextToDefaultActivity()..."); - TelecomAdapter.getInstance().rejectCall(mCall.getId(), true, message); + int phoneId = getActivePhoneId(); + Log.i(this, "sendTextToDefaultActivity()...phoneId:" + phoneId); + TelecomAdapter.getInstance().rejectCall(mCall[phoneId].getId(), true, message); onDismissDialog(); } @@ -302,4 +351,23 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> public void configureMessageDialog(List<String> textResponses); public Context getContext(); } + + @Override + public void onActiveSubChanged(int subId) { + final Call call = mCalls.getIncomingCall(); + int phoneId = CallList.getInstance().getPhoneId(subId); + if ((call != null) && (call.getId() == mCallId[phoneId])) { + Log.d(this, "Show incoming for call id: " + mCallId[phoneId] + " " + this); + if (showAnswerUi(true)) { + final List<String> textMsgs = mCalls.getTextResponses( + call.getId()); + configureAnswerTargetsForSms(call, textMsgs); + } + } else if ((call == null) && (mCalls.hasAnyLiveCall(subId))) { + Log.d(this, "Hide incoming for call id: " + mCallId[phoneId] + " " + this); + showAnswerUi(false); + } else { + Log.d(this, "No incoming call present for sub = " + subId + " " + this); + } + } } diff --git a/src/com/android/incallui/Call.java b/src/com/android/incallui/Call.java index ee73db2b..e73a4f47 100644 --- a/src/com/android/incallui/Call.java +++ b/src/com/android/incallui/Call.java @@ -30,6 +30,7 @@ import android.telecom.GatewayInfo; import android.telecom.InCallService.VideoCall; import android.telecom.PhoneAccountHandle; import android.telecom.VideoProfile; +import android.telephony.SubscriptionManager; import android.text.TextUtils; import java.util.ArrayList; @@ -241,6 +242,7 @@ public class Call { } }; + public boolean mIsActiveSub = false; private android.telecom.Call mTelecommCall; private final String mId; private int mState = State.INVALID; @@ -300,6 +302,7 @@ public class Call { Log.d(this, "updateFromTelecommCall: " + mTelecommCall.toString()); setState(translateState(mTelecommCall.getState())); setDisconnectCause(mTelecommCall.getDetails().getDisconnectCause()); + mIsActiveSub = mTelecommCall.isActive(); if (mTelecommCall.getVideoCall() != null) { if (mVideoCallCallback == null) { @@ -416,9 +419,29 @@ public class Call { int supportedCapabilities = mTelecommCall.getDetails().getCallCapabilities(); if ((capabilities & android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) { + if (InCallServiceImpl.isDsdaEnabled()) { + List<android.telecom.Call> conferenceableCalls = + mTelecommCall.getConferenceableCalls(); + boolean hasConferenceableCall = false; + if (!conferenceableCalls.isEmpty()){ + int subId = getSubId(); + for (android.telecom.Call call : conferenceableCalls) { + PhoneAccountHandle phHandle = call.getDetails().getAccountHandle(); + if ((phHandle != null) && ((Integer.parseInt(phHandle.getId())) == subId)) { + hasConferenceableCall = true; + break; + } + } + } + if (!hasConferenceableCall && + ((android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE + & supportedCapabilities) == 0)) { + // Cannot merge calls if there are no calls to merge with. + return false; + } // We allow you to merge if the capabilities allow it or if it is a call with // conferenceable calls. - if (mTelecommCall.getConferenceableCalls().isEmpty() && + } else if (mTelecommCall.getConferenceableCalls().isEmpty() && ((android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) { // Cannot merge calls if there are no calls to merge with. @@ -451,6 +474,22 @@ public class Call { return mTelecommCall == null ? null : mTelecommCall.getDetails().getAccountHandle(); } + public int getSubId() { + PhoneAccountHandle ph = getAccountHandle(); + if (ph != null) { + try { + if (ph.getId() != null ) { + return Integer.parseInt(getAccountHandle().getId()); + } + } catch (NumberFormatException e) { + Log.w(this,"sub Id is not a number " + e); + } + return SubscriptionManager.getDefaultVoiceSubId(); + } else { + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + } + public VideoCall getVideoCall() { return mTelecommCall == null ? null : mTelecommCall.getVideoCall(); } @@ -562,7 +601,7 @@ public class Call { } return String.format(Locale.US, "[%s, %s, %s, children:%s, parent:%s, conferenceable:%s, " + - "videoState:%s, mSessionModificationState:%d, VideoSettings:%s]", + "videoState:%s, mSessionModificationState:%d, VideoSettings:%s, mIsActivSub:%b]", mId, State.toString(getState()), android.telecom.Call.Details @@ -572,7 +611,7 @@ public class Call { this.mTelecommCall.getConferenceableCalls(), VideoProfile.videoStateToString(mTelecommCall.getDetails().getVideoState()), mSessionModificationState, - getVideoSettings()); + getVideoSettings(), mIsActiveSub); } public String toSimpleString() { diff --git a/src/com/android/incallui/CallButtonPresenter.java b/src/com/android/incallui/CallButtonPresenter.java index e1f45f01..dfcaca30 100644 --- a/src/com/android/incallui/CallButtonPresenter.java +++ b/src/com/android/incallui/CallButtonPresenter.java @@ -22,6 +22,8 @@ import android.content.Context; import android.os.Bundle; import android.telecom.CallAudioState; import android.telecom.InCallService.VideoCall; +import android.telecom.PhoneAccount; +import android.telecom.PhoneAccountHandle; import android.telecom.VideoProfile; import com.android.incallui.AudioModeProvider.AudioModeListener; @@ -39,7 +41,7 @@ import java.util.Objects; */ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButtonUi> implements InCallStateListener, AudioModeListener, IncomingCallListener, - InCallDetailsListener, CanAddCallListener, Listener { + InCallDetailsListener, CanAddCallListener, CallList.ActiveSubChangeListener, Listener { private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted"; private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state"; @@ -64,6 +66,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto inCallPresenter.addDetailsListener(this); inCallPresenter.addCanAddCallListener(this); inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this); + CallList.getInstance().addActiveSubChangeListener(this); // Update the buttons state immediately for the current call onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), @@ -80,6 +83,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto InCallPresenter.getInstance().removeDetailsListener(this); InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this); InCallPresenter.getInstance().removeCanAddCallListener(this); + CallList.getInstance().removeActiveSubChangeListener(this); } @Override @@ -380,7 +384,6 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto final boolean isCallOnHold = call.getState() == Call.State.ONHOLD; final boolean useExt = QtiCallUtils.useExt(ui.getContext()); - final boolean showAddCall = TelecomAdapter.getInstance().canAddCall(); final boolean showMerge = call.can( android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE); @@ -465,4 +468,11 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto } getUi().setCameraSwitched(!isUsingFrontFacingCamera); } + + public void onActiveSubChanged(int subId) { + InCallState state = InCallPresenter.getInstance() + .getPotentialStateFromCallList(CallList.getInstance()); + + onStateChange(null, state, CallList.getInstance()); + } } diff --git a/src/com/android/incallui/CallList.java b/src/com/android/incallui/CallList.java index c0014bdf..5b1855d7 100644 --- a/src/com/android/incallui/CallList.java +++ b/src/com/android/incallui/CallList.java @@ -24,8 +24,12 @@ import android.telecom.PhoneAccount; import com.android.contacts.common.testing.NeededForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import android.telecom.PhoneAccountHandle; +import android.telephony.SubscriptionManager; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -46,6 +50,8 @@ public class CallList { private static final int DISCONNECTED_CALL_LONG_TIMEOUT_MS = 5000; private static final int EVENT_DISCONNECTED_TIMEOUT = 1; + private static final int EVENT_NOTIFY_CHANGE = 2; + private static CallList sInstance = new CallList(); @@ -63,6 +69,9 @@ public class CallList { .newHashMap(); private final Set<Call> mPendingDisconnectCalls = Collections.newSetFromMap( new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1)); + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private final ArrayList<ActiveSubChangeListener> mActiveSubChangeListeners = + Lists.newArrayList(); /** * Static singleton accessor method. @@ -102,6 +111,18 @@ public class CallList { } } + int getPhoneId(int subId) { + int phoneId = SubscriptionManager.getPhoneId(subId); + if (phoneId >= InCallServiceImpl.sPhoneCount || phoneId < 0) { + phoneId = 0; + } + return phoneId; + } + + int[] getSubId(int phoneId) { + return SubscriptionManager.getSubId(phoneId); + } + /** * Called when a single call disconnects. */ @@ -119,6 +140,18 @@ public class CallList { * Called when a single call has changed. */ public void onIncoming(Call call, List<String> textMessages) { + Log.d(this, "onIncoming - " + call); + + // Update active subscription from call object. it will be set by + // Telecomm service for incoming call and whenever active sub changes. + if (call.mIsActiveSub) { + int sub = Integer.parseInt(call.getAccountHandle().getId()); + Log.d(this, "onIncoming - sub:" + sub + " mSubId:" + mSubId); + if (sub != mSubId) { + setActiveSubId(sub); + } + } + if (updateCallInMap(call)) { Log.i(this, "onIncoming - " + call); } @@ -140,6 +173,19 @@ public class CallList { */ public void onUpdate(Call call) { Trace.beginSection("onUpdate"); + PhoneAccountHandle ph = call.getAccountHandle(); + Log.d(this, "onUpdate - " + call + " ph:" + ph); + try { + if (call.mIsActiveSub && ph != null) { + int sub = Integer.parseInt(ph.getId()); + Log.d(this, "onUpdate - sub:" + sub + " mSubId:" + mSubId); + if(sub != mSubId) { + setActiveSubId(sub); + } + } + } catch (NumberFormatException e) { + Log.w(this,"Sub Id is not a number " + e); + } onUpdateCall(call); notifyGenericListeners(); Trace.endSection(); @@ -356,6 +402,13 @@ public class CallList { * TODO: Improve this logic to sort by call time. */ public Call getCallWithState(int state, int positionToFind) { + // if DSDA is enabled call getCallWithState with active subscription. + if (state != Call.State.SELECT_PHONE_ACCOUNT && getActiveSubId() + != SubscriptionManager.INVALID_SUBSCRIPTION_ID + && InCallServiceImpl.isDsdaEnabled()) { + return getCallWithState(state, positionToFind, getActiveSubId()); + } + Call retval = null; int position = 0; for (Call call : mCallById.values()) { @@ -559,6 +612,13 @@ public class CallList { Log.d(this, "EVENT_DISCONNECTED_TIMEOUT ", msg.obj); finishDisconnectedCall((Call) msg.obj); break; + case EVENT_NOTIFY_CHANGE: + Log.d(this, "EVENT_NOTIFY_CHANGE: "); + notifyGenericListeners(); + for (ActiveSubChangeListener listener : mActiveSubChangeListeners) { + listener.onActiveSubChanged(getActiveSubId()); + } + break; default: Log.wtf(this, "Message not expected: " + msg.what); break; @@ -612,4 +672,152 @@ public class CallList { */ public void onSessionModificationStateChange(int sessionModificationState); } + + /** + * Called when active subscription changes. + */ + void onActiveSubChanged(int activeSub) { + Log.d(this, "onActiveSubChanged = " + activeSub); + if (hasAnyLiveCall(activeSub)) { + setActiveSubId(activeSub); + } + } + + int getActiveSubId() { + return mSubId; + } + + /** + * Called to update the latest active subscription id, and also it + * notifies the registred clients about subscription change information. + */ + void setActiveSubId(int subId) { + if (subId != mSubId) { + Log.d(this, "setActiveSubId, oldActiveSubId = " + mSubId + + " newActiveSubId = " + subId); + mSubId = subId; + final Message msg = mHandler.obtainMessage(EVENT_NOTIFY_CHANGE, null); + mHandler.sendMessage(msg); + } + } + + /** + * Returns true, if any voice call is ACTIVE on the provided subscription. + */ + boolean hasAnyLiveCall(int subId) { + for (Call call : mCallById.values()) { + PhoneAccountHandle ph = call.getAccountHandle(); + try { + if (!isCallDead(call) && ph != null && (Integer.parseInt(ph.getId()) == subId)) { + Log.d(this, "hasAnyLiveCall sub = " + subId); + return true; + } + } catch (NumberFormatException e) { + Log.w(this,"Sub Id is not a number " + e); + } + } + Log.d(this, "no active call "); + return false; + } + + /** + * Returns true, if any call is ACTIVE + */ + boolean hasAnyLiveCall() { + for (Call call : mCallById.values()) { + if (!isCallDead(call)) { + Log.d(this, "hasAnyLiveCall call = " + call); + return true; + } + } + Log.d(this, "no active call "); + return false; + } + + /** + * This method checks whether any other subscription currently has active voice + * call other than current active subscription, if yes it makes that other + * subscription as active subscription i.e user visible subscription. + * @param retainLch whether to retain the LCH state of the other active sub + */ + boolean switchToOtherActiveSub() { + int activeSub = getActiveSubId(); + boolean subSwitched = false; + + for (int i = 0; i < InCallServiceImpl.sPhoneCount; i++) { + int[] subId = getSubId(i); + if ((subId[0] != activeSub) && hasAnyLiveCall(subId[0])) { + Log.d(this, "switchToOtherActiveSub, subId = " + subId[0]); + subSwitched = true; + TelecomAdapter.getInstance().switchToOtherActiveSub( + String.valueOf(subId[0])); + setActiveSubId(subId[0]); + break; + } + } + return subSwitched; + } + + /** + * Method to check if there is any live call in a sub other than the one supplied. + * @param currentSub The subscription to exclude while checking for active calls. + */ + boolean isAnyOtherSubActive(int currentSub) { + boolean result = false; + if(!InCallServiceImpl.isDsdaEnabled()) { + return false; + } + + for (int phoneId = 0; phoneId < InCallServiceImpl.sPhoneCount; + phoneId++) { + int[] subId = getSubId(phoneId); + + if ((subId[0] != currentSub) && hasAnyLiveCall(subId[0])) { + Log.d(this, "Live call found on another sub = " + subId[0]); + result = true; + break; + } + } + return result; + } + + /** + * Returns the [position]th call which belongs to provided subscription and + * found in the call map with the specified state. + */ + Call getCallWithState(int state, int positionToFind, int subId) { + Call retval = null; + int position = 0; + for (Call call : mCallById.values()) { + PhoneAccountHandle ph = call.getAccountHandle(); + try { + if ((call.getState() == state) && ((ph == null) || + (ph != null && (Integer.parseInt(ph.getId()) == subId)))) { + if (position >= positionToFind) { + retval = call; + break; + } else { + position++; + } + } + } catch (NumberFormatException e) { + Log.w(this,"Sub Id is not a number " + e); + } + } + return retval; + } + + void addActiveSubChangeListener(ActiveSubChangeListener listener) { + Preconditions.checkNotNull(listener); + mActiveSubChangeListeners.add(listener); + } + + void removeActiveSubChangeListener(ActiveSubChangeListener listener) { + Preconditions.checkNotNull(listener); + mActiveSubChangeListeners.remove(listener); + } + + interface ActiveSubChangeListener { + public void onActiveSubChanged(int subId); + } } diff --git a/src/com/android/incallui/ConferenceManagerFragment.java b/src/com/android/incallui/ConferenceManagerFragment.java index f5f52fcb..aaf41ade 100644 --- a/src/com/android/incallui/ConferenceManagerFragment.java +++ b/src/com/android/incallui/ConferenceManagerFragment.java @@ -98,10 +98,13 @@ public class ConferenceManagerFragment public void onVisibilityChanged(boolean isVisible) { mIsVisible = isVisible; ActionBar actionBar = getActivity().getActionBar(); + boolean isDsdaEnabled = InCallServiceImpl.isDsdaEnabled(); if (isVisible) { actionBar.setTitle(R.string.manageConferenceLabel); actionBar.setElevation(mActionBarElevation); - actionBar.setHideOffset(0); + if (!isDsdaEnabled) { + actionBar.setHideOffset(0); + } actionBar.show(); final CallList calls = CallList.getInstance(); @@ -111,7 +114,9 @@ public class ConferenceManagerFragment mConferenceParticipantList.requestFocus(); } else { actionBar.setElevation(0); - actionBar.setHideOffset(actionBar.getHeight()); + if (!isDsdaEnabled) { + actionBar.setHideOffset(actionBar.getHeight()); + } } } diff --git a/src/com/android/incallui/InCallActivity.java b/src/com/android/incallui/InCallActivity.java index 2f3020f2..7466c82d 100644 --- a/src/com/android/incallui/InCallActivity.java +++ b/src/com/android/incallui/InCallActivity.java @@ -17,6 +17,8 @@ package com.android.incallui; import android.app.ActionBar; +import android.app.FragmentTransaction; +import android.app.ActionBar.Tab; import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; @@ -30,6 +32,7 @@ import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.Point; import android.hardware.SensorManager; import android.os.Bundle; @@ -48,6 +51,8 @@ import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; +import android.widget.ImageView; +import android.widget.TextView; import com.android.phone.common.animation.AnimUtils; import com.android.phone.common.animation.AnimationListenerAdapter; @@ -116,6 +121,13 @@ public class InCallActivity extends Activity implements FragmentDisplayManager { private Animation mSlideOut; private boolean mDismissKeyguard = false; + private final int TAB_COUNT_ONE = 1; + private final int TAB_COUNT_TWO = 2; + private final int TAB_POSITION_FIRST = 0; + + private Tab[] mDsdaTab = new Tab[TAB_COUNT_TWO]; + private boolean[] mDsdaTabAdd = {false, false}; + AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() { @Override public void onAnimationEnd(Animation animation) { @@ -149,14 +161,19 @@ public class InCallActivity extends Activity implements FragmentDisplayManager { | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; getWindow().addFlags(flags); - - // Setup action bar for the conference call manager. - requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); - ActionBar actionBar = getActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.hide(); + boolean isDsdaEnabled = InCallServiceImpl.isDsdaEnabled(); + if (isDsdaEnabled) { + requestWindowFeature(Window.FEATURE_ACTION_BAR); + getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + getActionBar().setDisplayShowTitleEnabled(false); + getActionBar().setDisplayShowHomeEnabled(false); + } else { + requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); + if (getActionBar() != null) { + getActionBar().setDisplayHomeAsUpEnabled(true); + getActionBar().setDisplayShowTitleEnabled(true); + getActionBar().hide(); + } } // TODO(klp): Do we need to add this back when prox sensor is not available? @@ -216,6 +233,9 @@ public class InCallActivity extends Activity implements FragmentDisplayManager { mInCallOrientationEventListener = new InCallOrientationEventListener(this); + if (isDsdaEnabled ) { + initializeDsdaSwitchTab(); + } Log.d(this, "onCreate(): exit"); } @@ -859,6 +879,117 @@ public class InCallActivity extends Activity implements FragmentDisplayManager { } } + private void initializeDsdaSwitchTab() { + int phoneCount = InCallServiceImpl.sPhoneCount; + ActionBar bar = getActionBar(); + View[] mDsdaTabLayout = new View[phoneCount]; + TypedArray icons = getResources().obtainTypedArray(R.array.sim_icons); + int[] subString = {R.string.sub_1, R.string.sub_2}; + + for (int i = 0; i < phoneCount; i++) { + mDsdaTabLayout[i] = getLayoutInflater() + .inflate(R.layout.msim_tab_sub_info, null); + + ((ImageView)mDsdaTabLayout[i].findViewById(R.id.tabSubIcon)) + .setBackground(icons.getDrawable(i)); + + ((TextView)mDsdaTabLayout[i].findViewById(R.id.tabSubText)) + .setText(subString[i]); + + mDsdaTab[i] = bar.newTab().setCustomView(mDsdaTabLayout[i]) + .setTabListener(new TabListener(i)); + } + } + + public void updateDsdaTab() { + int phoneCount = InCallServiceImpl.sPhoneCount; + ActionBar bar = getActionBar(); + + for (int i = 0; i < phoneCount; i++) { + int[] subId = CallList.getInstance().getSubId(i); + if (subId != null && CallList.getInstance().hasAnyLiveCall(subId[0])) { + if (!mDsdaTabAdd[i]) { + addDsdaTab(i); + } + } else { + removeDsdaTab(i); + } + } + + updateDsdaTabSelection(); + } + + private void addDsdaTab(int subId) { + ActionBar bar = getActionBar(); + int tabCount = bar.getTabCount(); + + if (tabCount < subId) { + bar.addTab(mDsdaTab[subId], false); + } else { + bar.addTab(mDsdaTab[subId], subId, false); + } + mDsdaTabAdd[subId] = true; + Log.d(this, "addDsdaTab, subId = " + subId + " tab count = " + tabCount); + } + + private void removeDsdaTab(int subId) { + ActionBar bar = getActionBar(); + int tabCount = bar.getTabCount(); + + for (int i = 0; i < tabCount; i++) { + if (bar.getTabAt(i).equals(mDsdaTab[subId])) { + bar.removeTab(mDsdaTab[subId]); + mDsdaTabAdd[subId] = false; + return; + } + } + Log.d(this, "removeDsdaTab, subId = " + subId + " tab count = " + tabCount); + } + + private void updateDsdaTabSelection() { + ActionBar bar = getActionBar(); + int barCount = bar.getTabCount(); + + if (barCount == TAB_COUNT_ONE) { + bar.selectTab(bar.getTabAt(TAB_POSITION_FIRST)); + } else if (barCount == TAB_COUNT_TWO) { + int phoneId = CallList.getInstance().getPhoneId(CallList + .getInstance().getActiveSubId()); + bar.selectTab(bar.getTabAt(phoneId)); + } + } + + private class TabListener implements ActionBar.TabListener { + int mPhoneId; + + public TabListener(int phoneId) { + mPhoneId = phoneId; + } + + public void onTabSelected(Tab tab, FragmentTransaction ft) { + ActionBar bar = getActionBar(); + int tabCount = bar.getTabCount(); + Log.d(this, "onTabSelected mPhoneId:" + mPhoneId); + //Don't setActiveSubscription if tab count is 1.This is to avoid + //setting active subscription automatically when call on one sub + //ends and it's corresponding tab is removed.For such cases active + //subscription will be set by InCallPresenter.attemptFinishActivity. + int[] subId = CallList.getInstance().getSubId(mPhoneId); + if (tabCount != TAB_COUNT_ONE && CallList.getInstance().hasAnyLiveCall(subId[0]) + && (CallList.getInstance().getActiveSubId() != subId[0])) { + Log.d(this, "Switch to other active sub: " + subId[0]); + TelecomAdapter.getInstance().switchToOtherActiveSub( + String.valueOf(subId[0])); + } + } + + public void onTabUnselected(Tab tab, FragmentTransaction ft) { + } + + public void onTabReselected(Tab tab, FragmentTransaction ft) { + } + } + /** * Enables the OrientationEventListener if enable flag is true. Disables it if enable is * false diff --git a/src/com/android/incallui/InCallPresenter.java b/src/com/android/incallui/InCallPresenter.java index f4b5e751..3bf468f5 100644 --- a/src/com/android/incallui/InCallPresenter.java +++ b/src/com/android/incallui/InCallPresenter.java @@ -268,6 +268,14 @@ public class InCallPresenter implements CallList.Listener, private void attemptFinishActivity() { final boolean doFinish = (mInCallActivity != null && isActivityStarted()); Log.i(this, "Hide in call UI: " + doFinish); + + if ((mCallList != null) + && (InCallServiceImpl.isDsdaEnabled()) + && !(mCallList.hasAnyLiveCall(mCallList.getActiveSubId()))) { + Log.d(this, "Switch active sub"); + if (mCallList.switchToOtherActiveSub()) return; + } + if (doFinish) { mInCallActivity.setExcludeFromRecents(true); mInCallActivity.finish(); @@ -459,6 +467,9 @@ public class InCallPresenter implements CallList.Listener, callList.getOutgoingCall() != null; mInCallActivity.dismissKeyguard(hasCall); } + if (InCallServiceImpl.isDsdaEnabled() && (mInCallActivity != null)) { + mInCallActivity.updateDsdaTab(); + } } /** @@ -477,6 +488,10 @@ public class InCallPresenter implements CallList.Listener, for (IncomingCallListener listener : mIncomingCallListeners) { listener.onIncomingCall(oldState, mInCallState, call); } + + if (InCallServiceImpl.isDsdaEnabled() && (mInCallActivity != null)) { + mInCallActivity.updateDsdaTab(); + } } @Override @@ -1086,9 +1101,11 @@ public class InCallPresenter implements CallList.Listener, // TODO: Consider a proper state machine implementation + boolean isAnyOtherSubActive = InCallState.INCOMING == newState && + mCallList.isAnyOtherSubActive(mCallList.getActiveSubId()); // If the state isn't changing we have already done any starting/stopping of activities in // a previous pass...so lets cut out early - if (newState == mInCallState) { + if ((newState == mInCallState) && !(mInCallActivity == null && isAnyOtherSubActive)) { return newState; } @@ -1152,6 +1169,13 @@ public class InCallPresenter implements CallList.Listener, showCallUi |= InCallState.PENDING_OUTGOING == newState && mainUiNotVisible && isCallWithNoValidAccounts(mCallList.getPendingOutgoingCall()); + // Handle transition from InCallState.WAITING_FOR_ACCOUNT to InCallState.INCALL and + // and there is a call alive, this case can come for DSDA and hence we should show + // UI in such case. + showCallUi |= (newState == InCallState.INCALL) && + (mInCallState == InCallState.WAITING_FOR_ACCOUNT) && (mCallList.hasLiveCall() || + (mCallList.getBackgroundCall() != null)); + // The only time that we have an instance of mInCallActivity and it isn't started is // when it is being destroyed. In that case, lets avoid bringing up another instance of // the activity. When it is finally destroyed, we double check if we should bring it back @@ -1254,6 +1278,7 @@ public class InCallPresenter implements CallList.Listener, } private boolean startUi(InCallState inCallState) { + final Call incomingCall = mCallList.getIncomingCall(); boolean isCallWaiting = mCallList.getActiveCall() != null && mCallList.getIncomingCall() != null; @@ -1264,7 +1289,13 @@ public class InCallPresenter implements CallList.Listener, // There should be no jank from this since the screen is already off and will remain so // until our new activity is up. - if (isCallWaiting) { + // In addition to call waiting scenario, we need to force finish() in case of DSDA when + // we get an incoming call on one sub and there is a live call in other sub and screen + // is off. + boolean anyOtherSubActive = (incomingCall != null && + mCallList.isAnyOtherSubActive(mCallList.getActiveSubId())); + Log.d(this, "Start UI " + " anyOtherSubActive:" + anyOtherSubActive); + if (isCallWaiting || anyOtherSubActive) { if (mProximitySensor.isScreenReallyOff() && isActivityStarted()) { Log.i(this, "Restarting InCallActivity to turn screen on for call waiting"); mInCallActivity.finish(); diff --git a/src/com/android/incallui/InCallServiceImpl.java b/src/com/android/incallui/InCallServiceImpl.java index 89fa1326..ca080570 100644 --- a/src/com/android/incallui/InCallServiceImpl.java +++ b/src/com/android/incallui/InCallServiceImpl.java @@ -22,6 +22,7 @@ import android.os.IBinder; import android.telecom.Call; import android.telecom.CallAudioState; import android.telecom.InCallService; +import android.telephony.TelephonyManager; /** * Used to receive updates about calls from the Telecomm component. This service is bound to @@ -31,6 +32,9 @@ import android.telecom.InCallService; */ public class InCallServiceImpl extends InCallService { + static TelephonyManager mTelephonyManager; + static int sPhoneCount; + @Override public void onCallAudioStateChanged(CallAudioState audioState) { AudioModeProvider.getInstance().onAudioStateChanged(audioState); @@ -61,6 +65,8 @@ public class InCallServiceImpl extends InCallService { @Override public IBinder onBind(Intent intent) { final Context context = getApplicationContext(); + mTelephonyManager = TelephonyManager.from(context); + sPhoneCount = mTelephonyManager.getPhoneCount(); final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context); InCallPresenter.getInstance().setUp( getApplicationContext(), @@ -87,6 +93,14 @@ public class InCallServiceImpl extends InCallService { return false; } + static boolean isDsdaEnabled() { + if (mTelephonyManager.getMultiSimConfiguration() + == TelephonyManager.MultiSimVariants.DSDA) { + return true; + } + return false; + } + private void tearDown() { Log.v(this, "tearDown"); // Tear down the InCall system diff --git a/src/com/android/incallui/StatusBarNotifier.java b/src/com/android/incallui/StatusBarNotifier.java index 863ce41b..667a0db2 100644 --- a/src/com/android/incallui/StatusBarNotifier.java +++ b/src/com/android/incallui/StatusBarNotifier.java @@ -593,12 +593,18 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, // If a call is onhold during an incoming call, the call actually comes in as // INCOMING. For that case *and* traditional call-waiting, we want to // cancel the notification. + + // For DSDA, we want to cancel the notification if we get an incoming call on + // one sub and there is a live call on another sub. + CallList callList = CallList.getInstance(); boolean isCallWaiting = (call.getState() == Call.State.CALL_WAITING || (call.getState() == Call.State.INCOMING && - CallList.getInstance().getBackgroundCall() != null)); + (callList.getBackgroundCall() != null || + callList.isAnyOtherSubActive(callList.getActiveSubId())))); if (isCallWaiting) { - Log.i(this, "updateInCallNotification: call-waiting! force relaunch..."); + Log.i(this, "configureFullScreenIntent: call-waiting or dsda incoming call!" + + " force relaunch. Active sub:" + callList.getActiveSubId()); // Cancel the IN_CALL_NOTIFICATION immediately before // (re)posting it; this seems to force the // NotificationManager to launch the fullScreenIntent. diff --git a/src/com/android/incallui/TelecomAdapter.java b/src/com/android/incallui/TelecomAdapter.java index e4a39af1..14ce7c8d 100644 --- a/src/com/android/incallui/TelecomAdapter.java +++ b/src/com/android/incallui/TelecomAdapter.java @@ -120,6 +120,14 @@ final class TelecomAdapter implements InCallServiceListener { } } + void switchToOtherActiveSub(String subId) { + if (mInCallService != null) { + mInCallService.switchToOtherActiveSub(subId); + } else { + Log.e(this, "error switchToOtherActiveSub, mPhone is null"); + } + } + void separateCall(String callId) { android.telecom.Call call = getTelecommCallById(callId); if (call != null) { |