diff options
Diffstat (limited to 'InCallUI')
-rw-r--r-- | InCallUI/res/layout/incall_screen.xml | 10 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/AnswerFragment.java | 5 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/AnswerPresenter.java | 39 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/CallButtonPresenter.java | 33 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/CallCardPresenter.java | 45 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/CallList.java | 55 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/InCallActivity.java | 63 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/InCallPresenter.java | 119 |
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); + } } |