summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
authorSandeep Gutta <sangutta@codeaurora.org>2013-11-11 19:23:13 +0530
committerSandeep Gutta <sangutta@codeaurora.org>2013-11-25 14:57:09 +0530
commitd6e8ea435025aceadbf58ba78c4a2dd7fc745027 (patch)
treef33e72b81e5951dae3aa4a4679705b6892b4bed3 /src/com/android
parentf053a386531bb464cd95e7069f2bd0c058792941 (diff)
downloadandroid_packages_apps_InCallUI-d6e8ea435025aceadbf58ba78c4a2dd7fc745027.tar.gz
android_packages_apps_InCallUI-d6e8ea435025aceadbf58ba78c4a2dd7fc745027.tar.bz2
android_packages_apps_InCallUI-d6e8ea435025aceadbf58ba78c4a2dd7fc745027.zip
DSDA: Add InCallUI DSDA support.
-Add tab view support for DSDA -Add support to display voice calls based on the current active subscription. -Add few utilities in CallList, which required for handling voice calls across multiple subscription. Change-Id: Ib683f7c3b41ed3bb04367be1e9c331908bc46004
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/incallui/CallButtonPresenter.java13
-rw-r--r--src/com/android/incallui/CallCardFragment.java4
-rw-r--r--src/com/android/incallui/CallHandlerService.java2
-rw-r--r--src/com/android/incallui/CallList.java120
-rw-r--r--src/com/android/incallui/InCallActivity.java33
-rw-r--r--src/com/android/incallui/InCallPresenter.java24
-rw-r--r--src/com/android/incallui/msim/MSimAnswerFragment.java325
-rw-r--r--src/com/android/incallui/msim/MSimAnswerPresenter.java198
-rw-r--r--src/com/android/incallui/msim/MSimInCallActivity.java232
9 files changed, 937 insertions, 14 deletions
diff --git a/src/com/android/incallui/CallButtonPresenter.java b/src/com/android/incallui/CallButtonPresenter.java
index 1f0ee4d9..20049f76 100644
--- a/src/com/android/incallui/CallButtonPresenter.java
+++ b/src/com/android/incallui/CallButtonPresenter.java
@@ -35,7 +35,8 @@ import android.telephony.PhoneNumberUtils;
* Logic for call buttons.
*/
public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButtonUi>
- implements InCallStateListener, AudioModeListener, IncomingCallListener {
+ implements InCallStateListener, AudioModeListener, IncomingCallListener,
+ CallList.ActiveSubChangeListener {
private Call mCall;
private boolean mAutomaticallyMuted = false;
@@ -58,6 +59,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
// register for call state changes last
InCallPresenter.getInstance().addListener(this);
InCallPresenter.getInstance().addIncomingCallListener(this);
+ CallList.getInstance().addActiveSubChangeListener(this);
}
@Override
@@ -67,6 +69,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
InCallPresenter.getInstance().removeListener(this);
AudioModeProvider.getInstance().removeListener(this);
InCallPresenter.getInstance().removeIncomingCallListener(this);
+ CallList.getInstance().removeActiveSubChangeListener(this);
}
@Override
@@ -376,4 +379,12 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
void enableModifyCall(boolean enabled);
void showModifyCall(boolean show);
}
+
+ @Override
+ public void onActiveSubChanged(int subscription) {
+ InCallState state = InCallPresenter.getInstance()
+ .getPotentialStateFromCallList(CallList.getInstance());
+
+ onStateChange(state, CallList.getInstance());
+ }
}
diff --git a/src/com/android/incallui/CallCardFragment.java b/src/com/android/incallui/CallCardFragment.java
index 077538a4..3fb7ff0c 100644
--- a/src/com/android/incallui/CallCardFragment.java
+++ b/src/com/android/incallui/CallCardFragment.java
@@ -227,7 +227,9 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
showInternetCallLabel(isSipCall);
- if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
+ if (MSimTelephonyManager.getDefault().isMultiSimEnabled() &&
+ !(MSimTelephonyManager.getDefault().getMultiSimConfiguration()
+ == MSimTelephonyManager.MultiSimVariants.DSDA)) {
String[] sub = {"SUB 1", "SUB 2", "SUB 3"};
int subscription = getPresenter().getActiveSubscription();
diff --git a/src/com/android/incallui/CallHandlerService.java b/src/com/android/incallui/CallHandlerService.java
index a4825c6d..237e1234 100644
--- a/src/com/android/incallui/CallHandlerService.java
+++ b/src/com/android/incallui/CallHandlerService.java
@@ -51,7 +51,7 @@ public class CallHandlerService extends Service {
private static final int ON_ACTIVE_SUB_CHANGE = 11;
private static final int ON_UNSOL_CALLMODIFY = 12;
- private static final int LARGEST_MSG_ID = ON_DESTROY;
+ private static final int LARGEST_MSG_ID = ON_ACTIVE_SUB_CHANGE;
private CallList mCallList;
diff --git a/src/com/android/incallui/CallList.java b/src/com/android/incallui/CallList.java
index a6aef7e5..5decb9da 100644
--- a/src/com/android/incallui/CallList.java
+++ b/src/com/android/incallui/CallList.java
@@ -24,6 +24,8 @@ import com.google.common.base.Preconditions;
import android.os.Handler;
import android.os.Message;
+import android.telephony.MSimTelephonyManager;
+import com.android.internal.telephony.MSimConstants;
import com.android.services.telephony.common.Call;
import java.util.ArrayList;
@@ -43,6 +45,7 @@ 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();
@@ -54,6 +57,8 @@ public class CallList {
.newHashMap();
private int mSubscription = 0;
+ private final ArrayList<ActiveSubChangeListener> mActiveSubChangeListeners =
+ Lists.newArrayList();
/**
* Static singleton accessor method.
@@ -74,6 +79,8 @@ public class CallList {
public void onUpdate(Call call) {
Log.d(this, "onUpdate - ", call);
+ updateActiveSuscription();
+
updateCallInMap(call);
notifyListenersOfChange();
}
@@ -101,6 +108,8 @@ public class CallList {
public void onIncoming(Call call, List<String> textMessages) {
Log.d(this, "onIncoming - " + call);
+ updateActiveSuscription();
+
updateCallInMap(call);
updateCallTextMap(call, textMessages);
@@ -115,6 +124,8 @@ public class CallList {
public void onUpdate(List<Call> callsToUpdate) {
Log.d(this, "onUpdate(...)");
+ updateActiveSuscription();
+
Preconditions.checkNotNull(callsToUpdate);
for (Call call : callsToUpdate) {
Log.d(this, "\t" + call);
@@ -283,6 +294,11 @@ public class CallList {
* TODO: Improve this logic to sort by call time.
*/
public Call getCallWithState(int state, int positionToFind) {
+ if (MSimTelephonyManager.getDefault().getMultiSimConfiguration()
+ == MSimTelephonyManager.MultiSimVariants.DSDA) {
+ return getCallWithState(state, positionToFind, getActiveSubscription());
+ }
+
Call retval = null;
int position = 0;
for (Call call : mCallMap.values()) {
@@ -435,6 +451,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: ");
+ notifyListenersOfChange();
+ for (ActiveSubChangeListener listener : mActiveSubChangeListeners) {
+ listener.onActiveSubChanged(getActiveSubscription());
+ }
+ break;
default:
Log.wtf(this, "Message not expected: " + msg.what);
break;
@@ -480,12 +503,103 @@ public class CallList {
* Called when active subscription changes.
*/
public void onActiveSubChanged(int activeSub) {
- Log.d(this, "onActiveSubChanged: old = " + mSubscription + " new = " + activeSub);
-
- mSubscription = activeSub;
+ Log.d(this, "onActiveSubChanged = " + activeSub);
+ if (existsLiveCall(activeSub)) {
+ setActiveSubscription(activeSub);
+ }
}
public int getActiveSubscription() {
return mSubscription;
}
+
+ /**
+ * Called to update the latest active subscription id, and also it
+ * notifies the registred clients about subscription change information.
+ */
+ public void setActiveSubscription(int subscription) {
+ if (subscription != mSubscription) {
+ Log.i(this, "setActiveSubscription, old = " + mSubscription + " new = " + subscription);
+ mSubscription = subscription;
+ final Message msg = mHandler.obtainMessage(EVENT_NOTIFY_CHANGE, null);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /**
+ * Returns true, if any voice call in ACTIVE on the provided subscription.
+ */
+ public boolean existsLiveCall(int subscription) {
+ for (Call call : mCallMap.values()) {
+ if (!isCallDead(call) && (call.getSubscription() == subscription)) {
+ return true;
+ }
+ }
+ 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.
+ */
+ public boolean switchToOtherActiveSubscription() {
+ int activeSub = getActiveSubscription();
+ boolean subSwitched = false;
+
+ for (int i = 0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) {
+ if ((i != activeSub) && existsLiveCall(i)) {
+ Log.i(this, "switchToOtherActiveSubscription, sub = " + i);
+ subSwitched = true;
+ setActiveSubscription(i);
+ break;
+ }
+ }
+ return subSwitched;
+ }
+
+ /**
+ * Its a utility, gets the current active subscription from TeleService and
+ * updates the mSubscription member variable.
+ */
+ public void updateActiveSuscription() {
+ if (!MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
+ return;
+ }
+ setActiveSubscription(CallCommandClient.getInstance().getActiveSubscription());
+ }
+
+ /**
+ * Returns the [position]th call which belongs to provided subscription and
+ * found in the call map with the specified state.
+ */
+ public Call getCallWithState(int state, int positionToFind, int subscription) {
+ Call retval = null;
+ int position = 0;
+ for (Call call : mCallMap.values()) {
+ if ((call.getState() == state) && (call.getSubscription() == subscription)) {
+ if (position >= positionToFind) {
+ retval = call;
+ break;
+ } else {
+ position++;
+ }
+ }
+ }
+ return retval;
+ }
+
+ public void addActiveSubChangeListener(ActiveSubChangeListener listener) {
+ Preconditions.checkNotNull(listener);
+ mActiveSubChangeListeners.add(listener);
+ }
+
+ public void removeActiveSubChangeListener(ActiveSubChangeListener listener) {
+ Preconditions.checkNotNull(listener);
+ mActiveSubChangeListeners.remove(listener);
+ }
+
+ public interface ActiveSubChangeListener {
+ public void onActiveSubChanged(int subscription);
+ }
}
diff --git a/src/com/android/incallui/InCallActivity.java b/src/com/android/incallui/InCallActivity.java
index 92343d3e..80692f32 100644
--- a/src/com/android/incallui/InCallActivity.java
+++ b/src/com/android/incallui/InCallActivity.java
@@ -36,6 +36,7 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
+import android.telephony.MSimTelephonyManager;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
@@ -52,13 +53,13 @@ public class InCallActivity extends Activity {
private static final int INVALID_RES_ID = -1;
- private CallButtonFragment mCallButtonFragment;
- private CallCardFragment mCallCardFragment;
+ protected CallButtonFragment mCallButtonFragment;
+ protected CallCardFragment mCallCardFragment;
private AnswerFragment mAnswerFragment;
- private DialpadFragment mDialpadFragment;
- private ConferenceManagerFragment mConferenceManagerFragment;
+ protected DialpadFragment mDialpadFragment;
+ protected ConferenceManagerFragment mConferenceManagerFragment;
private boolean mIsForegroundActivity;
- private AlertDialog mDialog;
+ protected AlertDialog mDialog;
private AlertDialog mModifyCallPromptDialog;
/** Use to pass 'showDialpad' from {@link #onNewIntent} to {@link #onResume} */
@@ -70,6 +71,11 @@ public class InCallActivity extends Activity {
super.onCreate(icicle);
+ if (MSimTelephonyManager.getDefault().getMultiSimConfiguration()
+ == MSimTelephonyManager.MultiSimVariants.DSDA) {
+ return;
+ }
+
// set this flag so this activity will stay in front of the keyguard
// Have the WindowManager filter out touch events that are "too fat".
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
@@ -94,6 +100,11 @@ public class InCallActivity extends Activity {
Log.d(this, "onStart()...");
super.onStart();
+ if (MSimTelephonyManager.getDefault().getMultiSimConfiguration()
+ == MSimTelephonyManager.MultiSimVariants.DSDA) {
+ return;
+ }
+
// setting activity should be last thing in setup process
InCallPresenter.getInstance().setActivity(this);
}
@@ -148,7 +159,7 @@ public class InCallActivity extends Activity {
return mIsForegroundActivity;
}
- private boolean hasPendingErrorDialog() {
+ protected boolean hasPendingErrorDialog() {
return mDialog != null;
}
/**
@@ -168,6 +179,11 @@ public class InCallActivity extends Activity {
*/
@Override
public void finish() {
+ if (MSimTelephonyManager.getDefault().getMultiSimConfiguration()
+ == MSimTelephonyManager.MultiSimVariants.DSDA) {
+ super.finish();
+ return;
+ }
Log.i(this, "finish(). Dialog showing: " + (mDialog != null));
// skip finish if we are still showing a dialog.
@@ -347,7 +363,7 @@ public class InCallActivity extends Activity {
}
}
- private void initializeInCall() {
+ protected void initializeInCall() {
if (mCallButtonFragment == null) {
mCallButtonFragment = (CallButtonFragment) getFragmentManager()
.findFragmentById(R.id.callButtonFragment);
@@ -661,4 +677,7 @@ public class InCallActivity extends Activity {
Log.e(this, msg);
}
+ public void updateDsdaTab() {
+ Log.e(this, "updateDsdaTab : Not supported ");
+ }
}
diff --git a/src/com/android/incallui/InCallPresenter.java b/src/com/android/incallui/InCallPresenter.java
index 8ba186fa..d17e3e47 100644
--- a/src/com/android/incallui/InCallPresenter.java
+++ b/src/com/android/incallui/InCallPresenter.java
@@ -20,6 +20,8 @@
package com.android.incallui;
+import android.telephony.MSimTelephonyManager;
+
import com.android.incallui.service.PhoneNumberService;
import com.google.android.collect.Sets;
import com.google.common.base.Preconditions;
@@ -159,6 +161,11 @@ public class InCallPresenter implements CallList.Listener {
final boolean doFinish = (mInCallActivity != null && isActivityStarted());
Log.i(this, "Hide in call UI: " + doFinish);
+ if ((mCallList != null) && !(mCallList.existsLiveCall(mCallList.getActiveSubscription()))
+ && mCallList.switchToOtherActiveSubscription()) {
+ return;
+ }
+
if (doFinish) {
mInCallActivity.finish();
}
@@ -329,6 +336,11 @@ public class InCallPresenter implements CallList.Listener {
Log.d(this, "Notify " + listener + " of state " + mInCallState.toString());
listener.onStateChange(mInCallState, callList);
}
+
+ if (MSimTelephonyManager.getDefault().getMultiSimConfiguration()
+ == MSimTelephonyManager.MultiSimVariants.DSDA && (mInCallActivity != null)) {
+ mInCallActivity.updateDsdaTab();
+ }
}
/**
@@ -353,6 +365,11 @@ public class InCallPresenter implements CallList.Listener {
for (IncomingCallListener listener : mIncomingCallListeners) {
listener.onIncomingCall(mInCallState, call);
}
+
+ if (MSimTelephonyManager.getDefault().getMultiSimConfiguration()
+ == MSimTelephonyManager.MultiSimVariants.DSDA && (mInCallActivity != null)) {
+ mInCallActivity.updateDsdaTab();
+ }
}
/**
@@ -778,7 +795,12 @@ public class InCallPresenter implements CallList.Listener {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_NO_USER_ACTION);
- intent.setClass(mContext, InCallActivity.class);
+ if (MSimTelephonyManager.getDefault().getMultiSimConfiguration()
+ == MSimTelephonyManager.MultiSimVariants.DSDA) {
+ intent.setClass(mContext, MSimInCallActivity.class);
+ } else {
+ intent.setClass(mContext, InCallActivity.class);
+ }
if (showDialpad) {
intent.putExtra(InCallActivity.SHOW_DIALPAD_EXTRA, true);
}
diff --git a/src/com/android/incallui/msim/MSimAnswerFragment.java b/src/com/android/incallui/msim/MSimAnswerFragment.java
new file mode 100644
index 00000000..92ff5c2c
--- /dev/null
+++ b/src/com/android/incallui/msim/MSimAnswerFragment.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2013 The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2013 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.incallui;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ListView;
+
+import com.google.common.base.Preconditions;
+
+import java.util.ArrayList;
+
+/**
+ *
+ */
+public class MSimAnswerFragment extends BaseFragment<MSimAnswerPresenter,
+ MSimAnswerPresenter.AnswerUi>
+ implements GlowPadWrapper.AnswerListener, MSimAnswerPresenter.AnswerUi {
+
+ /**
+ * The popup showing the list of canned responses.
+ *
+ * This is an AlertDialog containing a ListView showing the possible choices. This may be null
+ * if the InCallScreen hasn't ever called showRespondViaSmsPopup() yet, or if the popup was
+ * visible once but then got dismissed.
+ */
+ private Dialog mCannedResponsePopup = null;
+
+ /**
+ * The popup showing a text field for users to type in their custom message.
+ */
+ private AlertDialog mCustomMessagePopup = null;
+
+ private ArrayAdapter<String> mTextResponsesAdapter = null;
+
+ private GlowPadWrapper mGlowpad;
+
+ public MSimAnswerFragment() {
+ }
+
+ @Override
+ public MSimAnswerPresenter createPresenter() {
+ return new MSimAnswerPresenter();
+ }
+
+ @Override
+ MSimAnswerPresenter.AnswerUi getUi() {
+ return this;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mGlowpad = (GlowPadWrapper) inflater.inflate(R.layout.answer_fragment,
+ container, false);
+
+ Log.d(this, "Creating view for answer fragment ", this);
+ Log.d(this, "Created from activity", getActivity());
+ mGlowpad.setAnswerListener(this);
+
+ return mGlowpad;
+ }
+
+ @Override
+ public void onDestroyView() {
+ Log.d(this, "onDestroyView");
+ if (mGlowpad != null) {
+ mGlowpad.stopPing();
+ mGlowpad = null;
+ }
+ super.onDestroyView();
+ }
+
+ @Override
+ public void showAnswerUi(boolean show) {
+ getView().setVisibility(show ? View.VISIBLE : View.GONE);
+
+ Log.d(this, "Show answer UI: " + show);
+ if (show) {
+ mGlowpad.startPing();
+ } else {
+ mGlowpad.stopPing();
+ }
+ }
+
+ @Override
+ public void showTextButton(boolean show) {
+ final int targetResourceId = show
+ ? R.array.incoming_call_widget_3way_targets
+ : R.array.incoming_call_widget_2way_targets;
+
+ if (targetResourceId != mGlowpad.getTargetResourceId()) {
+ if (show) {
+ // Answer, Decline, and Respond via SMS.
+ mGlowpad.setTargetResources(targetResourceId);
+ mGlowpad.setTargetDescriptionsResourceId(
+ R.array.incoming_call_widget_3way_target_descriptions);
+ mGlowpad.setDirectionDescriptionsResourceId(
+ R.array.incoming_call_widget_3way_direction_descriptions);
+ } else {
+ // Answer or Decline.
+ mGlowpad.setTargetResources(targetResourceId);
+ mGlowpad.setTargetDescriptionsResourceId(
+ R.array.incoming_call_widget_2way_target_descriptions);
+ mGlowpad.setDirectionDescriptionsResourceId(
+ R.array.incoming_call_widget_2way_direction_descriptions);
+ }
+
+ mGlowpad.reset(false);
+ }
+ }
+
+ @Override
+ public void showMessageDialog() {
+ final ListView lv = new ListView(getActivity());
+
+ Preconditions.checkNotNull(mTextResponsesAdapter);
+ lv.setAdapter(mTextResponsesAdapter);
+ lv.setOnItemClickListener(new RespondViaSmsItemClickListener());
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()).setCancelable(
+ true).setView(lv);
+ builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialogInterface) {
+ if (mGlowpad != null) {
+ mGlowpad.startPing();
+ }
+ }
+ });
+ mCannedResponsePopup = builder.create();
+ mCannedResponsePopup.show();
+ }
+
+ private boolean isCannedResponsePopupShowing() {
+ if (mCannedResponsePopup != null) {
+ return mCannedResponsePopup.isShowing();
+ }
+ return false;
+ }
+
+ private boolean isCustomMessagePopupShowing() {
+ if (mCustomMessagePopup != null) {
+ return mCustomMessagePopup.isShowing();
+ }
+ return false;
+ }
+
+ /**
+ * Dismiss the canned response list popup.
+ *
+ * This is safe to call even if the popup is already dismissed, and even if you never called
+ * showRespondViaSmsPopup() in the first place.
+ */
+ private void dismissCannedResponsePopup() {
+ if (mCannedResponsePopup != null) {
+ mCannedResponsePopup.dismiss(); // safe even if already dismissed
+ mCannedResponsePopup = null;
+ }
+ }
+
+ /**
+ * Dismiss the custom compose message popup.
+ */
+ private void dismissCustomMessagePopup() {
+ if (mCustomMessagePopup != null) {
+ mCustomMessagePopup.dismiss();
+ mCustomMessagePopup = null;
+ }
+ }
+
+ public void dismissPendingDialogues() {
+ if (isCannedResponsePopupShowing()) {
+ dismissCannedResponsePopup();
+ }
+
+ if (isCustomMessagePopupShowing()) {
+ dismissCustomMessagePopup();
+ }
+ }
+
+ public boolean hasPendingDialogs() {
+ return !(mCannedResponsePopup == null && mCustomMessagePopup == null);
+ }
+
+ /**
+ * Shows the custom message entry dialog.
+ */
+ public void showCustomMessageDialog() {
+ // Create an alert dialog containing an EditText
+ final EditText et = new EditText(getActivity());
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()).setCancelable(
+ true).setView(et)
+ .setPositiveButton(R.string.custom_message_send,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // The order is arranged in a way that the popup will be destroyed when the
+ // InCallActivity is about to finish.
+ final String textMessage = et.getText().toString().trim();
+ dismissCustomMessagePopup();
+ getPresenter().rejectCallWithMessage(textMessage);
+ }
+ })
+ .setNegativeButton(R.string.custom_message_cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismissCustomMessagePopup();
+ getPresenter().onDismissDialog();
+ }
+ })
+ .setTitle(R.string.respond_via_sms_custom_message);
+ mCustomMessagePopup = builder.create();
+
+ // Enable/disable the send button based on whether there is a message in the EditText
+ et.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ final Button sendButton = mCustomMessagePopup.getButton(
+ DialogInterface.BUTTON_POSITIVE);
+ sendButton.setEnabled(s != null && s.toString().trim().length() != 0);
+ }
+ });
+
+ // Keyboard up, show the dialog
+ mCustomMessagePopup.getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ mCustomMessagePopup.show();
+
+ // Send button starts out disabled
+ final Button sendButton = mCustomMessagePopup.getButton(DialogInterface.BUTTON_POSITIVE);
+ sendButton.setEnabled(false);
+ }
+
+ @Override
+ public void configureMessageDialog(ArrayList<String> textResponses) {
+ final ArrayList<String> textResponsesForDisplay = new ArrayList<String>(textResponses);
+
+ textResponsesForDisplay.add(getResources().getString(
+ R.string.respond_via_sms_custom_message));
+ mTextResponsesAdapter = new ArrayAdapter<String>(getActivity(),
+ android.R.layout.simple_list_item_1, android.R.id.text1, textResponsesForDisplay);
+ }
+
+ @Override
+ public void onAnswer(int callType) {
+ getPresenter().onAnswer(callType);
+ }
+
+ @Override
+ public void onDecline() {
+ getPresenter().onDecline();
+ }
+
+ @Override
+ public void onText() {
+ getPresenter().onText();
+ }
+
+ /**
+ * OnItemClickListener for the "Respond via SMS" popup.
+ */
+ public class RespondViaSmsItemClickListener implements AdapterView.OnItemClickListener {
+
+ /**
+ * Handles the user selecting an item from the popup.
+ */
+ @Override
+ public void onItemClick(AdapterView<?> parent, // The ListView
+ View view, // The TextView that was clicked
+ int position, long id) {
+ Log.d(this, "RespondViaSmsItemClickListener.onItemClick(" + position + ")...");
+ final String message = (String) parent.getItemAtPosition(position);
+ Log.v(this, "- message: '" + message + "'");
+ dismissCannedResponsePopup();
+
+ // The "Custom" choice is a special case.
+ // (For now, it's guaranteed to be the last item.)
+ if (position == (parent.getCount() - 1)) {
+ // Show the custom message dialog
+ showCustomMessageDialog();
+ } else {
+ getPresenter().rejectCallWithMessage(message);
+ }
+ }
+ }
+}
diff --git a/src/com/android/incallui/msim/MSimAnswerPresenter.java b/src/com/android/incallui/msim/MSimAnswerPresenter.java
new file mode 100644
index 00000000..375b05de
--- /dev/null
+++ b/src/com/android/incallui/msim/MSimAnswerPresenter.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2013 The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2013 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.incallui;
+
+import android.telephony.MSimTelephonyManager;
+import com.android.services.telephony.common.Call;
+
+import java.util.ArrayList;
+
+/**
+ * Presenter for the Incoming call widget.
+ */
+public class MSimAnswerPresenter extends Presenter<MSimAnswerPresenter.AnswerUi>
+ implements CallList.CallUpdateListener, CallList.Listener,
+ CallList.ActiveSubChangeListener {
+
+ private static final String TAG = MSimAnswerPresenter.class.getSimpleName();
+
+ private int mCallId[] = {Call.INVALID_CALL_ID, Call.INVALID_CALL_ID};
+ private Call mCall[] = {null, null};
+
+ @Override
+ public void onUiReady(AnswerUi ui) {
+ super.onUiReady(ui);
+
+ final CallList calls = CallList.getInstance();
+ final Call call = calls.getIncomingCall();
+ // TODO: change so that answer presenter never starts up if it's not incoming.
+ if (call != null) {
+ processIncomingCall(call);
+ }
+
+ // Listen for incoming calls.
+ calls.addListener(this);
+ CallList.getInstance().addActiveSubChangeListener(this);
+ }
+
+ @Override
+ public void onUiUnready(AnswerUi ui) {
+ super.onUiUnready(ui);
+
+ int subscription = CallList.getInstance().getActiveSubscription();
+ CallList.getInstance().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[subscription] != Call.INVALID_CALL_ID) {
+ CallList.getInstance().removeCallUpdateListener(mCallId[subscription], this);
+ }
+ CallList.getInstance().removeActiveSubChangeListener(this);
+ }
+
+ @Override
+ public void onCallListChange(CallList callList) {
+ // no-op
+ }
+
+ @Override
+ public void onDisconnect(Call call) {
+ // no-op
+ }
+
+ @Override
+ public void onIncomingCall(Call call) {
+ int subscription = call.getSubscription();
+ // TODO: Ui is being destroyed when the fragment detaches. Need clean up step to stop
+ // getting updates here.
+ Log.d(this, "onIncomingCall: " + this);
+ if (getUi() != null) {
+ if (call.getCallId() != mCallId[subscription]) {
+ // A new call is coming in.
+ processIncomingCall(call);
+ }
+ }
+ }
+
+ private void processIncomingCall(Call call) {
+ int subscription = call.getSubscription();
+ mCallId[subscription] = call.getCallId();
+ mCall[subscription] = call;
+
+ // Listen for call updates for the current call.
+ CallList.getInstance().addCallUpdateListener(mCallId[subscription], this);
+
+ Log.d(TAG, "Showing incoming for call id: " + mCallId[subscription] + " " + this);
+ final ArrayList<String> textMsgs = CallList.getInstance().getTextResponses(
+ call.getCallId());
+ getUi().showAnswerUi(true);
+
+ if (call.can(Call.Capabilities.RESPOND_VIA_TEXT) && textMsgs != null) {
+ getUi().showTextButton(true);
+ getUi().configureMessageDialog(textMsgs);
+ } else {
+ getUi().showTextButton(false);
+ }
+ }
+
+
+ @Override
+ public void onCallStateChanged(Call call) {
+ Log.d(this, "onCallStateChange() " + call + " " + this);
+ if (call.getState() != Call.State.INCOMING && call.getState() != Call.State.CALL_WAITING) {
+ int subscription = call.getSubscription();
+ // Stop listening for updates.
+ CallList.getInstance().removeCallUpdateListener(mCallId[subscription], this);
+
+ getUi().showAnswerUi(false);
+
+ // mCallId will hold the state of the call. We don't clear the mCall variable here as
+ // it may be useful for sending text messages after phone disconnects.
+ mCallId[subscription] = Call.INVALID_CALL_ID;
+ }
+ }
+
+ public void onAnswer(int callType) {
+ int subscription = CallList.getInstance().getActiveSubscription();
+ if (mCallId[subscription] == Call.INVALID_CALL_ID) {
+ return;
+ }
+
+ Log.d(this, "onAnswer " + mCallId[subscription]);
+
+ CallCommandClient.getInstance().answerCall(mCallId[subscription]);
+ }
+
+ public void onDecline() {
+ int subscription = CallList.getInstance().getActiveSubscription();
+ Log.d(this, "onDecline " + mCallId[subscription]);
+
+ CallCommandClient.getInstance().rejectCall(mCall[subscription], false, null);
+ }
+
+ public void onText() {
+ if (getUi() != null) {
+ getUi().showMessageDialog();
+ }
+ }
+
+ public void rejectCallWithMessage(String message) {
+ int subscription = CallList.getInstance().getActiveSubscription();
+ Log.d(this, "sendTextToDefaultActivity()...");
+
+ CallCommandClient.getInstance().rejectCall(mCall[subscription], true, message);
+
+ onDismissDialog();
+ }
+
+ public void onDismissDialog() {
+ InCallPresenter.getInstance().onDismissDialog();
+ }
+
+ interface AnswerUi extends Ui {
+ public void showAnswerUi(boolean show);
+ public void showTextButton(boolean show);
+ public void showMessageDialog();
+ public void configureMessageDialog(ArrayList<String> textResponses);
+ }
+
+ @Override
+ public void onActiveSubChanged(int subscription) {
+ final CallList calls = CallList.getInstance();
+ final Call call = calls.getIncomingCall();
+
+ if ((call != null) && (call.getCallId() == mCallId[subscription])) {
+ Log.i(TAG, "Show incoming for call id: " + mCallId[subscription] + " " + this);
+ final ArrayList<String> textMsgs = CallList.getInstance().getTextResponses(
+ call.getCallId());
+ getUi().showAnswerUi(true);
+
+ if (call.can(Call.Capabilities.RESPOND_VIA_TEXT) && textMsgs != null) {
+ getUi().showTextButton(true);
+ getUi().configureMessageDialog(textMsgs);
+ } else {
+ getUi().showTextButton(false);
+ }
+ } else if ((call == null) && (calls.existsLiveCall(subscription))) {
+ Log.i(TAG, "Hide incoming for call id: " + mCallId[subscription] + " " + this);
+ getUi().showAnswerUi(false);
+ }
+ }
+}
diff --git a/src/com/android/incallui/msim/MSimInCallActivity.java b/src/com/android/incallui/msim/MSimInCallActivity.java
new file mode 100644
index 00000000..3f61a595
--- /dev/null
+++ b/src/com/android/incallui/msim/MSimInCallActivity.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2013 The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2006 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.incallui;
+
+import android.app.ActionBar;
+import android.app.FragmentTransaction;
+import android.app.ActionBar.Tab;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.telephony.MSimTelephonyManager;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * Phone app "multisim in call" screen.
+ */
+public class MSimInCallActivity extends InCallActivity {
+
+ private MSimAnswerFragment mAnswerFragment;
+
+ 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};
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ Log.d(this, "onCreate()... this = " + this);
+
+ super.onCreate(icicle);
+
+ // set this flag so this activity will stay in front of the keyguard
+ // Have the WindowManager filter out touch events that are "too fat".
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);
+
+ requestWindowFeature(Window.FEATURE_ACTION_BAR);
+
+ getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ getActionBar().setDisplayShowTitleEnabled(false);
+ getActionBar().setDisplayShowHomeEnabled(false);
+
+ // Inflate everything in incall_screen.xml and add it to the screen.
+ setContentView(R.layout.incall_screen_msim);
+
+ initializeInCall();
+
+ initializeDsdaSwitchTab();
+ Log.d(this, "onCreate(): exit");
+ }
+
+ @Override
+ protected void onStart() {
+ Log.d(this, "onStart()...");
+ super.onStart();
+
+ // setting activity should be last thing in setup process
+ InCallPresenter.getInstance().setActivity(this);
+ }
+
+ @Override
+ public void finish() {
+ Log.i(this, "finish(). Dialog showing: " + (mDialog != null));
+
+ // skip finish if we are still showing a dialog.
+ if (!hasPendingErrorDialog() && !mAnswerFragment.hasPendingDialogs()) {
+ super.finish();
+ }
+ }
+
+ @Override
+ protected void initializeInCall() {
+ if (mCallButtonFragment == null) {
+ mCallButtonFragment = (CallButtonFragment) getFragmentManager()
+ .findFragmentById(R.id.callButtonFragment);
+ mCallButtonFragment.getView().setVisibility(View.INVISIBLE);
+ }
+
+ if (mCallCardFragment == null) {
+ mCallCardFragment = (CallCardFragment) getFragmentManager()
+ .findFragmentById(R.id.callCardFragment);
+ }
+
+ if (mAnswerFragment == null) {
+ mAnswerFragment = (MSimAnswerFragment) getFragmentManager()
+ .findFragmentById(R.id.answerFragment);
+ }
+
+ if (mDialpadFragment == null) {
+ mDialpadFragment = (DialpadFragment) getFragmentManager()
+ .findFragmentById(R.id.dialpadFragment);
+ mDialpadFragment.getView().setVisibility(View.INVISIBLE);
+ }
+
+ if (mConferenceManagerFragment == null) {
+ mConferenceManagerFragment = (ConferenceManagerFragment) getFragmentManager()
+ .findFragmentById(R.id.conferenceManagerFragment);
+ mConferenceManagerFragment.getView().setVisibility(View.INVISIBLE);
+ }
+ }
+
+ @Override
+ public void dismissPendingDialogs() {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ mAnswerFragment.dismissPendingDialogues();
+ }
+
+ private void initializeDsdaSwitchTab() {
+ int phoneCount = MSimTelephonyManager.getDefault().getPhoneCount();
+ 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));
+ }
+ }
+
+ @Override
+ public void updateDsdaTab() {
+ int phoneCount = MSimTelephonyManager.getDefault().getPhoneCount();
+ ActionBar bar = getActionBar();
+
+ for (int i = 0; i < phoneCount; i++) {
+ if (CallList.getInstance().existsLiveCall(i)) {
+ if (!mDsdaTabAdd[i]) {
+ addDsdaTab(i);
+ }
+ } else {
+ removeDsdaTab(i);
+ }
+ }
+
+ updateDsdaTabSelection();
+ }
+
+ private void addDsdaTab(int subscription) {
+ ActionBar bar = getActionBar();
+ int tabCount = bar.getTabCount();
+
+ if (tabCount < subscription) {
+ bar.addTab(mDsdaTab[subscription], false);
+ } else {
+ bar.addTab(mDsdaTab[subscription], subscription, false);
+ }
+ mDsdaTabAdd[subscription] = true;
+ }
+
+ private void removeDsdaTab(int subscription) {
+ ActionBar bar = getActionBar();
+ int tabCount = bar.getTabCount();
+
+ for (int i = 0; i < tabCount; i++) {
+ if (bar.getTabAt(i).equals(mDsdaTab[subscription])) {
+ bar.removeTab(mDsdaTab[subscription]);
+ mDsdaTabAdd[subscription] = false;
+ return;
+ }
+ }
+ }
+
+ 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) {
+ bar.selectTab(bar.getTabAt(CallList.getInstance().getActiveSubscription()));
+ }
+ }
+
+ private class TabListener implements ActionBar.TabListener {
+ int mSubscription;
+
+ public TabListener(int subId) {
+ mSubscription = subId;
+ }
+
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ ActionBar bar = getActionBar();
+
+ if (CallList.getInstance().existsLiveCall(mSubscription)) {
+ CallCommandClient.getInstance().setActiveSubscription(mSubscription);
+ }
+ }
+
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ }
+
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ }
+ }
+}