summaryrefslogtreecommitdiff
path: root/InCallUI
diff options
context:
space:
mode:
authorSantos Cordon <santoscordon@google.com>2013-08-05 02:17:28 -0700
committerSantos Cordon <santoscordon@google.com>2013-08-05 14:45:22 -0700
commit7ac6cb331919db2a3a8985e4cb3299b309d9012a (patch)
tree65cf7323fe8f355bfa7b80c21b1f0aca68bc2c49 /InCallUI
parent9a43aba1cbbff7f7d443afa327de5c5cd602e39f (diff)
Improve InCall State management
CL Contains the following changes: - InCallPresenter now manages an overall in-call state - Other presenters now listen to in-call state changes instead of getting updates from CallList directly. - Changes AnswerPresenter to a static fragment instead of dynamically loading it. It makes code easier and more aligned with other fragments used in the class. - Presenters now save the appropriate call and use callId() for commands sent to the telephony layer. - Extra logging in CallList - cleaned up the startup sequence in InCallActivity and moved everything to onCreate instead of onCreate & onResume Change-Id: I4ce4182eefcc134bfa16c33be8fe4aefc041f563
Diffstat (limited to 'InCallUI')
-rw-r--r--InCallUI/res/layout/incall_screen.xml10
-rw-r--r--InCallUI/src/com/android/incallui/AnswerFragment.java5
-rw-r--r--InCallUI/src/com/android/incallui/AnswerPresenter.java39
-rw-r--r--InCallUI/src/com/android/incallui/CallButtonPresenter.java33
-rw-r--r--InCallUI/src/com/android/incallui/CallCardPresenter.java45
-rw-r--r--InCallUI/src/com/android/incallui/CallList.java55
-rw-r--r--InCallUI/src/com/android/incallui/InCallActivity.java63
-rw-r--r--InCallUI/src/com/android/incallui/InCallPresenter.java119
8 files changed, 268 insertions, 101 deletions
diff --git a/InCallUI/res/layout/incall_screen.xml b/InCallUI/res/layout/incall_screen.xml
index 99319f4b4..9c364ed5c 100644
--- a/InCallUI/res/layout/incall_screen.xml
+++ b/InCallUI/res/layout/incall_screen.xml
@@ -30,6 +30,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
+
<fragment android:name="com.android.incallui.CallButtonFragment"
android:id="@+id/callButtonFragment"
android:layout_width="match_parent"
@@ -37,6 +38,13 @@
</LinearLayout>
- <!-- Glowpad fragment loaded here on demand. -->
+ <fragment android:name="com.android.incallui.AnswerFragment"
+ android:id="@+id/answerFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:gravity="center"
+ android:layout_gravity="bottom|center_horizontal" />
</FrameLayout>
diff --git a/InCallUI/src/com/android/incallui/AnswerFragment.java b/InCallUI/src/com/android/incallui/AnswerFragment.java
index cbdd8012e..0995ed665 100644
--- a/InCallUI/src/com/android/incallui/AnswerFragment.java
+++ b/InCallUI/src/com/android/incallui/AnswerFragment.java
@@ -52,6 +52,11 @@ public class AnswerFragment extends BaseFragment<AnswerPresenter> implements
}
@Override
+ public void showAnswerUi(boolean show) {
+ getView().setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @Override
public void onAnswer() {
getPresenter().onAnswer();
}
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index 66e371f20..c491c7e57 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -16,31 +16,60 @@
package com.android.incallui;
+import com.google.common.base.Preconditions;
+
import android.util.Log;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.services.telephony.common.Call;
+
/**
* Presenter for the Incoming call widget.
*/
-public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> {
+public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
+ implements InCallStateListener {
+ private static final String TAG = AnswerPresenter.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private Call mCall;
@Override
public void onUiReady(AnswerUi ui) {
super.onUiReady(ui);
}
+ @Override
+ public void onStateChange(InCallState state, CallList callList) {
+ if (state == InCallState.INCOMING) {
+ getUi().showAnswerUi(true);
+ mCall = callList.getIncomingCall();
+
+ if (DEBUG) {
+ Log.d(TAG, "Showing incoming with: " + mCall);
+ }
+ } else {
+ getUi().showAnswerUi(false);
+ mCall = null;
+ }
+ }
+
public void onAnswer() {
- // TODO(klp): hook in call id.
- CallCommandClient.getInstance().answerCall(1);
+ Preconditions.checkNotNull(mCall);
+
+ CallCommandClient.getInstance().answerCall(mCall.getCallId());
}
public void onDecline() {
- // TODO(klp): hook in call id.
- CallCommandClient.getInstance().disconnectCall(1);
+ Preconditions.checkNotNull(mCall);
+
+ CallCommandClient.getInstance().disconnectCall(mCall.getCallId());
}
public void onText() {
}
interface AnswerUi extends Ui {
+ public void showAnswerUi(boolean show);
}
}
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index 10d4f7237..014f8f8ae 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -16,15 +16,22 @@
package com.android.incallui;
+import com.google.common.base.Preconditions;
+
import android.media.AudioManager;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.services.telephony.common.Call;
+
/**
* Logic for call buttons.
*/
public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButtonUi>
- implements CallList.Listener {
+ implements InCallStateListener {
private AudioManager mAudioManager;
+ private Call mCall;
public void init(AudioManager audioManager) {
mAudioManager = audioManager;
@@ -35,22 +42,24 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
super.onUiReady(ui);
getUi().setMute(mAudioManager.isMicrophoneMute());
getUi().setSpeaker(mAudioManager.isSpeakerphoneOn());
-
- CallList.getInstance().addListener(this);
}
@Override
- public void onCallListChange(CallList callList) {
- // show the buttons if there is a live call AND there is no
- // incoming call.
- final boolean showButtons = callList.existsLiveCall() &&
- callList.getIncomingCall() == null;
- getUi().setVisible(showButtons);
+ public void onStateChange(InCallState state, CallList callList) {
+ getUi().setVisible(state == InCallState.INCALL);
+
+ if (state == InCallState.INCALL) {
+ mCall = callList.getActiveCall();
+ } else {
+ mCall = null;
+ }
}
public void endCallClicked() {
+ Preconditions.checkNotNull(mCall);
+
// TODO(klp): hook up call id.
- CallCommandClient.getInstance().disconnectCall(1);
+ CallCommandClient.getInstance().disconnectCall(mCall.getCallId());
// TODO(klp): Remove once all state is gathered from CallList.
// This will be wrong when you disconnect from a call if
@@ -76,8 +85,10 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
}
public void holdClicked(boolean checked) {
+ Preconditions.checkNotNull(mCall);
+
// TODO(klp): use appropriate hold callId.
- CallCommandClient.getInstance().hold(1, true);
+ CallCommandClient.getInstance().hold(mCall.getCallId(), checked);
getUi().setHold(checked);
}
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 0ba4176c3..d5632a6f0 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -16,40 +16,55 @@
package com.android.incallui;
+import android.util.Log;
+
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
import com.android.services.telephony.common.Call;
/**
* Presenter for the Call Card Fragment.
- * This class listens for changes to CallList and passes it along to the fragment.
+ * This class listens for changes to InCallState and passes it along to the fragment.
*/
public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
- implements CallList.Listener {
+ implements InCallStateListener {
+ private static final String TAG = CallCardPresenter.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@Override
public void onUiReady(CallCardUi ui) {
super.onUiReady(ui);
-
- CallList.getInstance().addListener(this);
}
@Override
- public void onCallListChange(CallList callList) {
+ public void onStateChange(InCallState state, CallList callList) {
final CallCardUi ui = getUi();
- // Populate the primary call card based on the incoming call or the active call.
- final Call call = callList.getIncomingOrActive();
- if (call != null) {
- ui.setNumber(call.getNumber());
+ Call primary = null;
+ Call secondary = null;
+
+ if (state == InCallState.INCOMING) {
+ primary = callList.getIncomingCall();
+ } else if (state == InCallState.INCALL) {
+ primary = callList.getActiveCall();
+ secondary = callList.getBackgroundCall();
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Primary call: " + primary);
+ Log.d(TAG, "Secondary call: " + secondary);
+ }
+
+ // Set primary call data
+ if (primary != null) {
+ ui.setNumber(primary.getNumber());
} else {
- // When there is no longer an incoming/active call, we need to reset everything
- // so that no data survives for the next call.
ui.setNumber("");
}
- // secondary call card info only comes from the background call (if any exist)
- final Call backgroundCall = callList.getBackgroundCall();
- if (backgroundCall != null) {
- ui.setSecondaryCallInfo(true, backgroundCall.getNumber());
+ // Set secondary call data
+ if (secondary != null) {
+ ui.setSecondaryCallInfo(true, secondary.getNumber());
} else {
ui.setSecondaryCallInfo(false, null);
}
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index abc250afc..5852ec723 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -21,19 +21,35 @@ import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import android.util.Log;
import com.android.services.telephony.common.Call;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
- * Maintains the list of active calls received from CallHandlerService.
- * TODO(klp): This class should be used by InCallUI to read about
- * changes to calls.
+ * Maintains the list of active calls received from CallHandlerService and notifies interested
+ * classes of changes to the call list as they are received from the telephony stack.
+ * Primary lister of changes to this class is InCallPresenter.
*/
public class CallList {
+ private static final String TAG = CallList.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final Map<Integer, String> STATE_MAP = ImmutableMap.<Integer, String>builder()
+ .put(Call.State.ACTIVE, "ACTIVE")
+ .put(Call.State.CALL_WAITING, "CALL_WAITING")
+ .put(Call.State.DIALING, "DIALING")
+ .put(Call.State.IDLE, "IDLE")
+ .put(Call.State.INCOMING, "INCOMING")
+ .put(Call.State.ONHOLD, "ONHOLD")
+ .put(Call.State.INVALID, "INVALID")
+ .build();
+
private static CallList sInstance;
private final HashMap<Integer, Call> mCallMap = Maps.newHashMap();
@@ -59,8 +75,9 @@ public class CallList {
* Called when a single call has changed.
*/
public void onUpdate(Call call) {
- updateCallInMap(call);
+ logD("onUpdate - " + safeCallString(call));
+ updateCallInMap(call);
notifyListenersOfChange();
}
@@ -68,8 +85,12 @@ public class CallList {
* Called when multiple calls have changed.
*/
public void onUpdate(List<Call> callsToUpdate) {
+ logD("onUpdate(...)");
+
Preconditions.checkNotNull(callsToUpdate);
for (Call call : callsToUpdate) {
+ logD("\t" + safeCallString(call));
+
updateCallInMap(call);
}
@@ -98,11 +119,15 @@ public class CallList {
public Call getIncomingOrActive() {
Call retval = getIncomingCall();
if (retval == null) {
- retval = getFirstCallWithState(Call.State.ACTIVE);
+ retval = getActiveCall();
}
return retval;
}
+ public Call getActiveCall() {
+ return getFirstCallWithState(Call.State.ACTIVE);
+ }
+
public Call getBackgroundCall() {
return getFirstCallWithState(Call.State.ONHOLD);
}
@@ -132,6 +157,8 @@ public class CallList {
}
}
+ logD("Found " + (retval == null ? "no " : "") + "call with state: " +
+ STATE_MAP.get(state));
return retval;
}
@@ -162,6 +189,24 @@ public class CallList {
return Call.State.IDLE == state || Call.State.INVALID == state;
}
+ private void logD(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, msg);
+ }
+ }
+
+ /**
+ * Creates a log-safe string for call objects.
+ */
+ private String safeCallString(Call call) {
+ final StringBuffer buffer = new StringBuffer();
+ buffer.append("Call (")
+ .append(call.getCallId())
+ .append("), ")
+ .append(STATE_MAP.get(call.getState()));
+ return buffer.toString();
+ }
+
/**
* Listener interface for any class that wants to be notified of changes
* to the call list.
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index f7663e3c7..f63b52ef8 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -18,8 +18,6 @@ package com.android.incallui;
import android.app.Activity;
import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -33,16 +31,13 @@ import android.widget.Toast;
* Phone app "in call" screen.
*/
public class InCallActivity extends Activity {
-
private static final String TAG = InCallActivity.class.getSimpleName();
-
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private CallButtonFragment mCallButtonFragment;
private CallCardFragment mCallCardFragment;
private AnswerFragment mAnswerFragment;
- private boolean mFragmentsAdded = false;
@Override
protected void onCreate(Bundle icicle) {
@@ -67,32 +62,10 @@ public class InCallActivity extends Activity {
logD("onCreate(): exit");
}
-
@Override
protected void onResume() {
logD("onResume()...");
super.onResume();
-
- if (!mFragmentsAdded) {
- getFragmentManager().beginTransaction()
- .add(R.id.main, mAnswerFragment)
- .commit();
-
- mFragmentsAdded = true;
- }
-
- InCallPresenter.getInstance().setActivity(this);
- }
-
- @Override
- public void onAttachFragment(Fragment fragment) {
- if (fragment instanceof AnswerFragment) {
- mAnswerFragment = (AnswerFragment) fragment;
-
- getFragmentManager().beginTransaction()
- .hide(mAnswerFragment)
- .commit();
- }
}
// onPause is guaranteed to be called when the InCallActivity goes
@@ -133,7 +106,11 @@ public class InCallActivity extends Activity {
@Override
public void finish() {
logD("finish()...");
- moveTaskToBack(true);
+ super.finish();
+
+ // TODO(klp): Actually finish the activity for now. Revisit performance implications of
+ // this before launch.
+ // moveTaskToBack(true);
}
@Override
@@ -216,20 +193,6 @@ public class InCallActivity extends Activity {
return super.onKeyDown(keyCode, event);
}
- /**
- * Called to show the incoming call widget.
- */
- /* package */ void showIncoming(boolean show) {
- final FragmentTransaction trans = getFragmentManager().beginTransaction();
- if (show) {
- trans.show(mAnswerFragment);
- } else {
- trans.hide(mAnswerFragment);
- }
-
- trans.commitAllowingStateLoss();
- }
-
private void initializeInCall() {
// TODO(klp): Make sure that this doesn't need to move back to onResume() since they are
// statically added fragments.
@@ -244,8 +207,22 @@ public class InCallActivity extends Activity {
}
if (mAnswerFragment == null) {
- mAnswerFragment = new AnswerFragment();
+ mAnswerFragment = (AnswerFragment) getFragmentManager()
+ .findFragmentById(R.id.answerFragment);
}
+
+ setUpPresenters();
+ }
+
+ private void setUpPresenters() {
+ InCallPresenter mainPresenter = InCallPresenter.getInstance();
+
+ mainPresenter.addListener(mCallButtonFragment.getPresenter());
+ mainPresenter.addListener(mCallCardFragment.getPresenter());
+ mainPresenter.addListener(mAnswerFragment.getPresenter());
+
+ // setting activity should be last thing in setup process
+ mainPresenter.setActivity(this);
}
private void toast(String text) {
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 74f898d68..b73e65a32 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -16,25 +16,33 @@
package com.android.incallui;
+import com.google.android.collect.Sets;
import com.google.common.base.Preconditions;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
+import java.util.Set;
/**
* Takes updates from the CallList and notifies the InCallActivity (UI)
- * of the changes. Also responsible for starting the InCallActivity when a call comes in.
+ * of the changes.
+ * Responsible for starting the activity for a new call and finishing the activity when all calls
+ * are disconnected.
+ * Creates and manages the in-call state and provides a listener pattern for the presenters
+ * that want to listen in on the in-call state changes.
*/
public class InCallPresenter implements CallList.Listener {
private static final String TAG = InCallPresenter.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static InCallPresenter sInCallPresenter;
private Context mContext;
private InCallState mInCallState = InCallState.HIDDEN;
private InCallActivity mInCallActivity;
+ private final Set<InCallStateListener> mListeners = Sets.newHashSet();
public static synchronized InCallPresenter getInstance() {
if (sInCallPresenter == null) {
@@ -53,39 +61,91 @@ public class InCallPresenter implements CallList.Listener {
public void setActivity(InCallActivity inCallActivity) {
mInCallActivity = inCallActivity;
+ mInCallState = InCallState.STARTED;
- mInCallActivity.showIncoming(CallList.getInstance().getIncomingCall() != null);
+ // Since the UI just came up, imitate an update from the call list
+ // to set the proper UI state.
+ onCallListChange(CallList.getInstance());
}
/**
- * Called when there is a change to the call list. Responsible for starting and hiding
- * the InCall UI.
+ * Called when there is a change to the call list.
+ * Sets the In-Call state for the entire in-call app based on the information it gets from
+ * CallList. Dispatches the in-call state to all listeners. Can trigger the creation or
+ * destruction of the UI based on the states that is calculates.
*/
@Override
public void onCallListChange(CallList callList) {
- // TODO: Organize this code a little better. Too hard to read.
+ // fast fail if we are still starting up
+ if (mInCallState == InCallState.STARTING_UP) {
+ return;
+ }
- final boolean showInCall = callList.existsLiveCall();
+ InCallState newState = mInCallState;
+ if (callList.getIncomingCall() != null) {
+ newState = InCallState.INCOMING;
+ } else if (callList.getActiveCall() != null) {
+ newState = InCallState.INCALL;
+ } else {
+ newState = InCallState.HIDDEN;
+ }
- if (showInCall && mInCallState == InCallState.HIDDEN) {
+ newState = startOrFinishUi(newState);
- // TODO(klp): Update the flags to match the PhoneApp activity
- final Intent intent = new Intent(mContext, InCallActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
+ // finally set the new state before announcing it to the world
+ mInCallState = newState;
- } else if (!showInCall && mInCallState == InCallState.SHOWING_INCALL) {
- if (mInCallActivity != null) {
- mInCallActivity.finish();
- }
+ // notify listeners of new state
+ for (InCallStateListener listener : mListeners) {
+ logD("Notify " + listener + " of state " + mInCallState.toString());
+ listener.onStateChange(mInCallState, callList);
}
+ }
+
+ public void addListener(InCallStateListener listener) {
+ Preconditions.checkNotNull(listener);
+ mListeners.add(listener);
+ }
+
+ public void removeListener(InCallStateListener listener) {
+ Preconditions.checkNotNull(listener);
+ mListeners.remove(listener);
+ }
+
+ /**
+ * When the state of in-call changes, this is the first method to get called. It determines if
+ * the UI needs to be started or finished depending on the new state and does it.
+ * It returns a potential new middle state (STARTING_UP) if appropriate.
+ */
+ private InCallState startOrFinishUi(InCallState newState) {
+ // TODO(klp): Consider a proper state machine implementation
+
+ // if we need to show something, we need to start the Ui...
+ if (newState != InCallState.HIDDEN) {
- if (mInCallActivity != null) {
- boolean showIncoming = callList.getIncomingCall() != null;
- mInCallActivity.showIncoming(showIncoming);
+ // ...only if the UI is currently hidden
+ if (mInCallState == InCallState.HIDDEN) {
+ // TODO(klp): Update the flags to match the PhoneApp activity
+ final Intent intent = new Intent(mContext, InCallActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+
+ return InCallState.STARTING_UP;
+ }
+
+ // (newState == InCallState.HIDDEN)
+ // Else, we need to hide the UI...if it exists
+ } else if (mInCallActivity != null) {
+ mListeners.clear();
+
+ // Null out reference before we start end sequence
+ InCallActivity temp = mInCallActivity;
+ mInCallActivity = null;
+
+ temp.finish();
}
- mInCallState = showInCall ? InCallState.SHOWING_INCALL : InCallState.HIDDEN;
+ return newState;
}
/**
@@ -95,11 +155,28 @@ public class InCallPresenter implements CallList.Listener {
CallList.getInstance().addListener(this);
}
+ private void logD(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, msg);
+ }
+ }
+
/**
* All the main states of InCallActivity.
*/
- private enum InCallState {
+ public enum InCallState {
HIDDEN,
- SHOWING_INCALL
+ STARTING_UP,
+ STARTED,
+ INCOMING,
+ INCALL
};
+
+ /**
+ * Interface implemented by classes that need to know about the InCall State.
+ */
+ public interface InCallStateListener {
+ // TODO(klp): Enhance state to contain the call objects instead of passing CallList
+ public void onStateChange(InCallState state, CallList callList);
+ }
}