summaryrefslogtreecommitdiff
path: root/InCallUI
diff options
context:
space:
mode:
authorSantos Cordon <santoscordon@google.com>2013-09-05 13:22:07 -0700
committerSantos Cordon <santoscordon@google.com>2013-09-09 12:18:12 -0700
commit1f63d2d8e8db3616c16886952813b3d0473216e7 (patch)
tree9ffb5c9a744cc5679c59091e617c00acc4209193 /InCallUI
parent24deb0f78f1854bfc273bb888e7fd4ac123bcc22 (diff)
Improve lifecycle ordering for InCallUI
This CL rearranges startup and teardown of InCallUI to reduce the number of race conditions resulting in runtime exceptions and app crashes. At a high level this CL fixes the following: - TeleService should be able to unbind and rebind at any time without causing problems in the UI. (Fixes to InCallPresenter) - On weird occasions we were seeing secondary UIs pop up if we rebound while the older UI was still up and one of the UIs would be orphaned on the foreground. - call notifications can be sent during (1) activity startup, (2) activity lifetime, (3) activity destruction, (4) no activity...and nothing should crash. - Lots of crashes when notifications came during destruction and startup. (Fixed setup in InCallActivity + presenters, and startup/teardown ordering in InCallPresenter) Details: (1) InCallPresenter handed out instances of member classes to the UI classes, but upon unbinding and rebinding, the classes were recreated. This meant that the activity which was potentially still up had stale versions of AudioModeProvider and ProximitySensor. - Classes created/used by CallHandlerService are now singletons so that they do not change from one bind to the other. If the service tries to initialize InCallPresenter while the activity is still up (and so we haven't yet torn down) we reuse the reuse the previous initialization since there is no need to recreate them, and classes in the Activity dont ever become stale. (2) We were recreating new copies of InCallActivity on updates that occur while tearing down the previous activity. This caused weird errors where second emptier activities were up after all calls ended. - Solution to this was to ignore all updates while we are finishing the activity. When the activity is finally finished, we check if we should bring up a new activity in case some update came while we were finishing. (3) We set listeners on presenters from a parent class that wasn't aware of UI transitions. - All Presenters are not responsible for setting and unsetting their listeners. This allows them to unset them before their UI goes away. + renamed HIDDEN to NO_CALLS as hidden was confusing to developers which associated the term with foreground/backgroundness of the app. + Improved some logging bug:10573125 Change-Id: I2d1af6a6e972b3b3bd93af839054e879f0b74b4f
Diffstat (limited to 'InCallUI')
-rw-r--r--InCallUI/src/com/android/incallui/AnswerPresenter.java2
-rw-r--r--InCallUI/src/com/android/incallui/AudioModeProvider.java5
-rw-r--r--InCallUI/src/com/android/incallui/BaseFragment.java2
-rw-r--r--InCallUI/src/com/android/incallui/CallButtonPresenter.java47
-rw-r--r--InCallUI/src/com/android/incallui/CallCardPresenter.java25
-rw-r--r--InCallUI/src/com/android/incallui/CallHandlerService.java26
-rw-r--r--InCallUI/src/com/android/incallui/InCallActivity.java37
-rw-r--r--InCallUI/src/com/android/incallui/InCallPresenter.java131
-rw-r--r--InCallUI/src/com/android/incallui/Presenter.java10
-rw-r--r--InCallUI/src/com/android/incallui/ProximitySensor.java40
-rw-r--r--InCallUI/src/com/android/incallui/StatusBarNotifier.java2
11 files changed, 172 insertions, 155 deletions
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index 7f27133ed..607e966d3 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -48,6 +48,7 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
@Override
public void onUiUnready(AnswerUi ui) {
super.onUiUnready(ui);
+
CallList.getInstance().removeListener(this);
// This is necessary because the activity can be destroyed while an incoming call exists.
@@ -55,6 +56,7 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
if (mCallId != Call.INVALID_CALL_ID) {
CallList.getInstance().removeCallUpdateListener(mCallId, this);
}
+
}
@Override
diff --git a/InCallUI/src/com/android/incallui/AudioModeProvider.java b/InCallUI/src/com/android/incallui/AudioModeProvider.java
index 8224d3e20..870087565 100644
--- a/InCallUI/src/com/android/incallui/AudioModeProvider.java
+++ b/InCallUI/src/com/android/incallui/AudioModeProvider.java
@@ -28,13 +28,14 @@ import java.util.List;
*/
/* package */ class AudioModeProvider {
- private static AudioModeProvider sAudioModeProvider;
+ private static AudioModeProvider sAudioModeProvider = new AudioModeProvider();
private int mAudioMode = AudioMode.EARPIECE;
private boolean mMuted = false;
private int mSupportedModes = AudioMode.ALL_MODES;
private final List<AudioModeListener> mListeners = Lists.newArrayList();
- public AudioModeProvider() {
+ public static AudioModeProvider getInstance() {
+ return sAudioModeProvider;
}
public void onAudioModeChange(int newMode, boolean muted) {
diff --git a/InCallUI/src/com/android/incallui/BaseFragment.java b/InCallUI/src/com/android/incallui/BaseFragment.java
index ae207f3a1..6c2ba216c 100644
--- a/InCallUI/src/com/android/incallui/BaseFragment.java
+++ b/InCallUI/src/com/android/incallui/BaseFragment.java
@@ -52,6 +52,6 @@ public abstract class BaseFragment<T extends Presenter<U>, U extends Ui> extends
@Override
public void onDestroyView() {
super.onDestroyView();
- mPresenter.onUiUnready(getUi());
+ mPresenter.onUiDestroy(getUi());
}
}
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index 17273a874..b2884e625 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -30,7 +30,6 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
implements InCallStateListener, AudioModeListener {
private Call mCall;
- private AudioModeProvider mAudioModeProvider;
private ProximitySensor mProximitySensor;
private boolean mAutomaticallyMuted = false;
private boolean mPreviousMuteState = false;
@@ -42,16 +41,22 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
public void onUiReady(CallButtonUi ui) {
super.onUiReady(ui);
- if (mAudioModeProvider != null) {
- mAudioModeProvider.addListener(this);
- }
+ mProximitySensor = InCallPresenter.getInstance().getProximitySensor();
+ AudioModeProvider.getInstance().addListener(this);
+
+ // register for call state changes last
+ InCallPresenter.getInstance().addListener(this);
}
@Override
public void onUiUnready(CallButtonUi ui) {
- if (mAudioModeProvider != null) {
- mAudioModeProvider.removeListener(this);
- }
+ InCallPresenter.getInstance().removeListener(this);
+ AudioModeProvider.getInstance().removeListener(this);
+
+ mProximitySensor = null;
+
+ // set Ui to null, so should go last
+ super.onUiUnready(ui);
}
@Override
@@ -89,18 +94,11 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
}
public int getAudioMode() {
- if (mAudioModeProvider != null) {
- return mAudioModeProvider.getAudioMode();
- }
- return AudioMode.EARPIECE;
+ return AudioModeProvider.getInstance().getAudioMode();
}
public int getSupportedAudio() {
- if (mAudioModeProvider != null) {
- return mAudioModeProvider.getSupportedModes();
- }
-
- return 0;
+ return AudioModeProvider.getInstance().getSupportedModes();
}
public void setAudioMode(int mode) {
@@ -174,7 +172,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
public void addCallClicked() {
// Automatically mute the current call
mAutomaticallyMuted = true;
- mPreviousMuteState = mAudioModeProvider.getMute();
+ mPreviousMuteState = AudioModeProvider.getInstance().getMute();
getUi().setMute(true);
CallCommandClient.getInstance().addCall();
@@ -217,7 +215,8 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
ui.showAddCall(call.can(Capabilities.ADD_CALL));
// Restore the previous mute state
- if (mAutomaticallyMuted && mAudioModeProvider.getMute() != mPreviousMuteState) {
+ if (mAutomaticallyMuted &&
+ AudioModeProvider.getInstance().getMute() != mPreviousMuteState) {
ui.setMute(mPreviousMuteState);
mAutomaticallyMuted = false;
}
@@ -252,18 +251,6 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
}
}
- public void setAudioModeProvider(AudioModeProvider audioModeProvider) {
- // AudioModeProvider works effectively as a pass through. However, if we
- // had this presenter listen for changes directly, it would have to live forever
- // or risk missing important updates.
- mAudioModeProvider = audioModeProvider;
- mAudioModeProvider.addListener(this);
- }
-
- public void setProximitySensor(ProximitySensor proximitySensor) {
- mProximitySensor = proximitySensor;
- }
-
public interface CallButtonUi extends Ui {
void setVisible(boolean on);
void setMute(boolean on);
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index ee587067f..801e7493f 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -49,7 +49,6 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
private static final long CALL_TIME_UPDATE_INTERVAL = 1000; // in milliseconds
private PhoneNumberService mPhoneNumberService;
- private AudioModeProvider mAudioModeProvider;
private Call mPrimary;
private Call mSecondary;
private ContactCacheEntry mPrimaryContactInfo;
@@ -94,23 +93,26 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
public void onUiReady(CallCardUi ui) {
super.onUiReady(ui);
+ AudioModeProvider.getInstance().addListener(this);
+
// Contact search may have completed before ui is ready.
if (mPrimaryContactInfo != null) {
updatePrimaryDisplayInfo(mPrimaryContactInfo, false);
}
- if (mAudioModeProvider != null) {
- mAudioModeProvider.addListener(this);
- }
+ // Register for call state changes last
+ InCallPresenter.getInstance().addListener(this);
}
@Override
public void onUiUnready(CallCardUi ui) {
super.onUiUnready(ui);
- if (mAudioModeProvider != null) {
- mAudioModeProvider.removeListener(this);
- }
+ // stop getting call state changes
+ InCallPresenter.getInstance().removeListener(this);
+
+ AudioModeProvider.getInstance().removeListener(this);
+
mPrimary = null;
mPrimaryContactInfo = null;
mSecondaryContactInfo = null;
@@ -184,8 +186,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
// Set the call state
if (mPrimary != null) {
- final boolean bluetoothOn = mAudioModeProvider != null &&
- mAudioModeProvider.getAudioMode() == AudioMode.BLUETOOTH;
+ final boolean bluetoothOn =
+ (AudioModeProvider.getInstance().getAudioMode() == AudioMode.BLUETOOTH);
ui.setCallState(mPrimary.getState(), mPrimary.getDisconnectCause(), bluetoothOn);
} else {
ui.setCallState(Call.State.IDLE, Call.DisconnectCause.UNKNOWN, false);
@@ -492,11 +494,6 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
return contactInfo.number;
}
- public void setAudioModeProvider(AudioModeProvider audioModeProvider) {
- mAudioModeProvider = audioModeProvider;
- mAudioModeProvider.addListener(this);
- }
-
public void secondaryPhotoClicked() {
CallCommandClient.getInstance().swap();
}
diff --git a/InCallUI/src/com/android/incallui/CallHandlerService.java b/InCallUI/src/com/android/incallui/CallHandlerService.java
index b73135590..b2b76f2fa 100644
--- a/InCallUI/src/com/android/incallui/CallHandlerService.java
+++ b/InCallUI/src/com/android/incallui/CallHandlerService.java
@@ -56,20 +56,21 @@ public class CallHandlerService extends Service {
@Override
public void onCreate() {
- Log.d(this, "onCreate started");
+ Log.i(this, "creating");
super.onCreate();
mMainHandler = new MainHandler();
mCallList = CallList.getInstance();
- mAudioModeProvider = new AudioModeProvider();
+ mAudioModeProvider = AudioModeProvider.getInstance();
mInCallPresenter = InCallPresenter.getInstance();
+
mInCallPresenter.setUp(getApplicationContext(), mCallList, mAudioModeProvider);
Log.d(this, "onCreate finished");
}
@Override
public void onDestroy() {
- Log.d(this, "onDestroy started");
+ Log.i(this, "destroying");
// Remove all pending messages before nulling out handler
for (int i = 1; i <= LARGEST_MSG_ID; i++) {
@@ -94,14 +95,17 @@ public class CallHandlerService extends Service {
@Override
public IBinder onBind(Intent intent) {
- Log.d(this, "onBind");
+ Log.i(this, "onBind");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
- Log.d(this, "onUnbind");
- return true;
+ Log.i(this, "onUnbind");
+
+ // Returning true here means we get called on rebind, which is a feature we do not need.
+ // Return false so that all reconections happen with a call to onBind().
+ return false;
}
private final ICallHandlerService.Stub mBinder = new ICallHandlerService.Stub() {
@@ -119,7 +123,7 @@ public class CallHandlerService extends Service {
@Override
public void onDisconnect(Call call) {
try {
- Log.d(CallHandlerService.this, "onDisconnected: " + call);
+ Log.i(CallHandlerService.this, "onDisconnected: " + call);
mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_DISCONNECT_CALL, call));
} catch (Exception e) {
Log.e(TAG, "Error processing onDisconnect() call.", e);
@@ -129,7 +133,7 @@ public class CallHandlerService extends Service {
@Override
public void onIncoming(Call call, List<String> textResponses) {
try {
- Log.d(CallHandlerService.this, "onIncomingCall: " + call);
+ Log.i(CallHandlerService.this, "onIncomingCall: " + call);
// TODO(klp): Add text responses to the call object.
Map.Entry<Call, List<String>> incomingCall
@@ -145,7 +149,7 @@ public class CallHandlerService extends Service {
@Override
public void onUpdate(List<Call> calls) {
try {
- Log.d(CallHandlerService.this, "onUpdate " + calls.toString());
+ Log.i(CallHandlerService.this, "onUpdate " + calls.toString());
// TODO(klp): Add use of fullUpdate to message
mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_UPDATE_MULTI_CALL, calls));
@@ -157,7 +161,7 @@ public class CallHandlerService extends Service {
@Override
public void onAudioModeChange(int mode, boolean muted) {
try {
- Log.d(CallHandlerService.this, "onAudioModeChange : " + AudioMode.toString(mode));
+ Log.i(CallHandlerService.this, "onAudioModeChange : " + AudioMode.toString(mode));
mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_AUDIO_MODE, mode,
muted ? 1 : 0, null));
} catch (Exception e) {
@@ -168,7 +172,7 @@ public class CallHandlerService extends Service {
@Override
public void onSupportedAudioModeChange(int modeMask) {
try {
- Log.d(CallHandlerService.this, "onSupportedAudioModeChange : " + AudioMode.toString(
+ Log.i(CallHandlerService.this, "onSupportedAudioModeChange : " + AudioMode.toString(
modeMask));
mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_SUPPORTED_AUDIO_MODE,
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index 6cae6b05e..fb9f99c10 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -58,14 +58,17 @@ public class InCallActivity extends Activity {
// Inflate everything in incall_screen.xml and add it to the screen.
setContentView(R.layout.incall_screen);
+ initializeInCall();
Log.d(this, "onCreate(): exit");
}
@Override
protected void onStart() {
+ Log.d(this, "onStart()...");
super.onStart();
- initializeInCall();
+ // setting activity should be last thing in setup process
+ InCallPresenter.getInstance().setActivity(this);
}
@Override
@@ -98,7 +101,7 @@ public class InCallActivity extends Activity {
protected void onDestroy() {
Log.d(this, "onDestroy()... this = " + this);
- tearDownPresenters();
+ InCallPresenter.getInstance().setActivity(null);
super.onDestroy();
}
@@ -293,36 +296,6 @@ public class InCallActivity extends Activity {
.findFragmentById(R.id.conferenceManagerFragment);
mConferenceManagerFragment.getView().setVisibility(View.INVISIBLE);
}
- setUpPresenterCallbacks();
- }
-
- private void setUpPresenterCallbacks() {
- InCallPresenter mainPresenter = InCallPresenter.getInstance();
-
- mCallButtonFragment.getPresenter().setAudioModeProvider(
- mainPresenter.getAudioModeProvider());
- mCallButtonFragment.getPresenter().setProximitySensor(
- mainPresenter.getProximitySensor());
- final CallCardPresenter presenter = mCallCardFragment.getPresenter();
- presenter.setAudioModeProvider(mainPresenter.getAudioModeProvider());
-
- mainPresenter.addListener(mCallButtonFragment.getPresenter());
- mainPresenter.addListener(mCallCardFragment.getPresenter());
- mainPresenter.addListener(mConferenceManagerFragment.getPresenter());
-
- // setting activity should be last thing in setup process
- mainPresenter.setActivity(this);
- }
-
- private void tearDownPresenters() {
- Log.d(this, "Tearing down presenters.");
- InCallPresenter mainPresenter = InCallPresenter.getInstance();
-
- mainPresenter.removeListener(mCallButtonFragment.getPresenter());
- mainPresenter.removeListener(mCallCardFragment.getPresenter());
- mainPresenter.removeListener(mConferenceManagerFragment.getPresenter());
-
- mainPresenter.setActivity(null);
}
private void toast(String text) {
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index cc4cee277..1e2f6755d 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -51,7 +51,7 @@ public class InCallPresenter implements CallList.Listener {
private CallList mCallList;
private InCallActivity mInCallActivity;
private boolean mServiceConnected = false;
- private InCallState mInCallState = InCallState.HIDDEN;
+ private InCallState mInCallState = InCallState.NO_CALLS;
private ProximitySensor mProximitySensor;
public static synchronized InCallPresenter getInstance() {
@@ -62,12 +62,18 @@ public class InCallPresenter implements CallList.Listener {
}
public void setUp(Context context, CallList callList, AudioModeProvider audioModeProvider) {
+ if (mServiceConnected) {
+ Log.i(this, "New service connection replacing existing one.");
+ // retain the current resources, no need to create new ones.
+ Preconditions.checkState(context == mContext);
+ Preconditions.checkState(callList == mCallList);
+ Preconditions.checkState(audioModeProvider == mAudioModeProvider);
+ return;
+ }
+
Preconditions.checkNotNull(context);
mContext = context;
- mCallList = callList;
- mCallList.addListener(this);
-
mContactInfoCache = ContactInfoCache.getInstance(context);
mStatusBarNotifier = new StatusBarNotifier(context, mContactInfoCache, mCallList);
@@ -76,11 +82,17 @@ public class InCallPresenter implements CallList.Listener {
mAudioModeProvider = audioModeProvider;
+ mProximitySensor = new ProximitySensor(context, mAudioModeProvider);
+ addListener(mProximitySensor);
+
+ mCallList = callList;
+
// This only gets called by the service so this is okay.
mServiceConnected = true;
- mProximitySensor = new ProximitySensor(context, mAudioModeProvider);
- addListener(mProximitySensor);
+ // The final thing we do in this set up is add ourselves as a listener to CallList. This
+ // will kick off an update and the whole process can start.
+ mCallList.addListener(this);
Log.d(this, "Finished InCallPresenter.setUp");
}
@@ -105,18 +117,42 @@ public class InCallPresenter implements CallList.Listener {
* the tear-down process.
*/
public void setActivity(InCallActivity inCallActivity) {
- mInCallActivity = inCallActivity;
-
- if (mInCallActivity != null) {
- Log.i(this, "UI Initialized");
+ boolean updateListeners = false;
+
+ if (inCallActivity != null) {
+ if (mInCallActivity == null) {
+ updateListeners = true;
+ Log.i(this, "UI Initialized");
+ } else if (mInCallActivity != inCallActivity) {
+ Log.wtf(this, "Setting a second activity before destroying the first.");
+ } else {
+ // since setActivity is called onStart(), it can be called multiple times.
+ // This is fine and ignorable, but we do not want to update the world every time
+ // this happens (like going to/from background) so we do not set updateListeners.
+ }
- // Since the UI just came up, imitate an update from the call list
- // to set the proper UI state.
- onCallListChange(mCallList);
+ mInCallActivity = inCallActivity;
} else {
Log.i(this, "UI Destroyed)");
+ updateListeners = true;
+ mInCallActivity = null;
attemptCleanup();
}
+
+
+ // Messages can come from the telephony layer while the activity is coming up
+ // and while the activity is going down. So in both cases we need to recalculate what
+ // state we should be in after they complete.
+ // Examples: (1) A new incoming call could come in and then get disconnected before
+ // the activity is created.
+ // (2) All calls could disconnect and then get a new incoming call before the
+ // activity is destroyed.
+ //
+ // ... but only if we are still connected (or got a new connection) to the service.
+ // Otherwise the service will come back up when it needs us.
+ if (updateListeners && mServiceConnected) {
+ onCallListChange(mCallList);
+ }
}
/**
@@ -148,7 +184,10 @@ public class InCallPresenter implements CallList.Listener {
*/
@Override
public void onIncomingCall(Call call) {
- mInCallState = startOrFinishUi(InCallState.INCOMING);
+ InCallState newState = startOrFinishUi(InCallState.INCOMING);
+
+ Log.i(this, "Phone switching state: " + mInCallState + " -> " + newState);
+ mInCallState = newState;
for (IncomingCallListener listener : mIncomingCallListeners) {
listener.onIncomingCall(call);
@@ -159,7 +198,7 @@ public class InCallPresenter implements CallList.Listener {
* Given the call list, return the state in which the in-call screen should be.
*/
public static InCallState getPotentialStateFromCallList(CallList callList) {
- InCallState newState = InCallState.HIDDEN;
+ InCallState newState = InCallState.NO_CALLS;
if (callList.getIncomingCall() != null) {
newState = InCallState.INCOMING;
@@ -219,8 +258,7 @@ public class InCallPresenter implements CallList.Listener {
* Returns true if the incall app is the foreground application.
*/
public boolean isShowingInCallUi() {
- return (mInCallActivity != null &&
- mInCallActivity.isForegroundActivity());
+ return (isActivityStarted() && mInCallActivity.isForegroundActivity());
}
/**
@@ -257,9 +295,9 @@ public class InCallPresenter implements CallList.Listener {
// 1. there is an activity
// 2. the activity is not already in the foreground
// 3. We are in a state where we want to show the incall ui
- if (mInCallActivity != null &&
- !mInCallActivity.isForegroundActivity() &&
- mInCallState != InCallState.HIDDEN) {
+ if (isActivityStarted() &&
+ !isShowingInCallUi() &&
+ mInCallState != InCallState.NO_CALLS) {
showInCall();
}
}
@@ -312,35 +350,34 @@ public class InCallPresenter implements CallList.Listener {
// user with a top-level notification. Just show the call UI normally.
final boolean showCallUi = (InCallState.OUTGOING == newState);
+ // TODO: Can we be suddenly in a call without it having been in the outgoing or incoming
+ // state? I havent seen that but if it can happen, the code below should be enabled.
+ // showCallUi |= (InCallState.INCALL && !isActivityStarted());
+
+ // 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
+ // up so we aren't going to lose anything by avoiding a second startup here.
+ boolean activityIsFinishing = mInCallActivity != null && !isActivityStarted();
+ if (activityIsFinishing) {
+ Log.i(this, "Undo the state change: " + newState + " -> " + mInCallState);
+ return mInCallState;
+ }
+
if (showCallUi) {
Log.i(this, "Start in call UI");
showInCall();
} else if (startStartupSequence) {
Log.i(this, "Start Full Screen in call UI");
mStatusBarNotifier.updateNotificationAndLaunchIncomingCallUi(newState, mCallList);
- } else if (newState == InCallState.HIDDEN) {
+ } else if (newState == InCallState.NO_CALLS) {
Log.i(this, "Hide in call UI");
- // The new state is the hidden state (no calls). Tear everything down.
+ // The new state is the no calls state. Tear everything down.
if (mInCallActivity != null) {
- if (mInCallActivity.isFinishing()) {
- // Tear down process:
- // When there are no more calls to handle, two things happen:
- // 1. The binding connection with TeleService ends
- // 2. InCallState changes to HIDDEN and we call activity.finish() here.
- //
- // Without the service connection, we will not get updates from the service
- // and so will never get a new call to move out of the HIDDEN state. Since this
- // code is called when we move from a different state into the HIDDEN state,
- // it should never get hit twice. In case it does, log an error.
- Log.e(this, "Attempting to finish incall activity twice.");
- } else {
+ if (isActivityStarted()) {
mInCallActivity.finish();
}
-
- // blow away stale contact info so that we get fresh data on
- // the next set of calls
- mContactInfoCache.clearCache();
}
}
@@ -352,8 +389,16 @@ public class InCallPresenter implements CallList.Listener {
* down.
*/
private void attemptCleanup() {
- if (mInCallActivity == null && !mServiceConnected) {
- Log.i(this, "Start InCall presenter cleanup.");
+ boolean shouldCleanup = (mInCallActivity == null && !mServiceConnected);
+ Log.i(this, "attemptCleanup? " + shouldCleanup);
+
+ if (shouldCleanup) {
+
+ // blow away stale contact info so that we get fresh data on
+ // the next set of calls
+ mContactInfoCache.clearCache();
+ mContactInfoCache = null;
+
mProximitySensor = null;
mAudioModeProvider = null;
@@ -398,7 +443,7 @@ public class InCallPresenter implements CallList.Listener {
*/
public enum InCallState {
// InCall Screen is off and there are no calls
- HIDDEN,
+ NO_CALLS,
// Incoming-call screen is up
INCOMING,
@@ -413,10 +458,6 @@ public class InCallPresenter implements CallList.Listener {
return (this == INCOMING);
}
- public boolean isHidden() {
- return (this == HIDDEN);
- }
-
public boolean isConnectingOrConnected() {
return (this == INCOMING ||
this == OUTGOING ||
diff --git a/InCallUI/src/com/android/incallui/Presenter.java b/InCallUI/src/com/android/incallui/Presenter.java
index b4962aea6..d2f2d36f8 100644
--- a/InCallUI/src/com/android/incallui/Presenter.java
+++ b/InCallUI/src/com/android/incallui/Presenter.java
@@ -35,10 +35,18 @@ public abstract class Presenter<U extends Ui> {
/**
* Called when the UI view is destroyed in Fragment.onDestroyView().
*/
- public void onUiUnready(U ui) {
+ public final void onUiDestroy(U ui) {
+ onUiUnready(ui);
mUi = null;
}
+ /**
+ * To be overriden by Presenter implementations. Called when the fragment is being
+ * destroyed but before ui is set to null.
+ */
+ public void onUiUnready(U ui) {
+ }
+
public U getUi() {
return mUi;
}
diff --git a/InCallUI/src/com/android/incallui/ProximitySensor.java b/InCallUI/src/com/android/incallui/ProximitySensor.java
index 4befb2b25..c28a69e5e 100644
--- a/InCallUI/src/com/android/incallui/ProximitySensor.java
+++ b/InCallUI/src/com/android/incallui/ProximitySensor.java
@@ -24,6 +24,7 @@ import com.android.incallui.AudioModeProvider.AudioModeListener;
import com.android.incallui.InCallPresenter.InCallState;
import com.android.incallui.InCallPresenter.InCallStateListener;
import com.android.services.telephony.common.AudioMode;
+import com.google.common.base.Objects;
/**
* Class manages the proximity sensor for the in-call UI.
@@ -83,13 +84,17 @@ public class ProximitySensor implements AccelerometerListener.OrientationListene
public void onStateChange(InCallState state, CallList callList) {
// We ignore incoming state because we do not want to enable proximity
// sensor during incoming call screen
- mIsPhoneOffhook = (InCallState.INCALL == state
+ boolean isOffhook = (InCallState.INCALL == state
|| InCallState.OUTGOING == state);
- mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;
- mAccelerometerListener.enable(mIsPhoneOffhook);
+ if (isOffhook != mIsPhoneOffhook) {
+ mIsPhoneOffhook = isOffhook;
- updateProximitySensorMode();
+ mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;
+ mAccelerometerListener.enable(mIsPhoneOffhook);
+
+ updateProximitySensorMode();
+ }
}
@Override
@@ -169,17 +174,10 @@ public class ProximitySensor implements AccelerometerListener.OrientationListene
* 4) If the slider is open(i.e. the hardkeyboard is *not* hidden)
*/
private void updateProximitySensorMode() {
- Log.i(this, "updateProximitySensorMode");
-
if (proximitySensorModeEnabled()) {
- Log.i(this, "keyboard open: " + mIsHardKeyboardOpen);
- Log.i(this, "dialpad visible: " + mDialpadVisible);
- Log.v(this, "isOffhook: ", mIsPhoneOffhook);
-
synchronized (mProximityWakeLock) {
final int audioMode = mAudioModeProvider.getAudioMode();
- Log.i(this, "audioMode: " + AudioMode.toString(audioMode));
// turn proximity sensor off and turn screen on immediately if
// we are using a headset, the keyboard is open, or the device
@@ -194,7 +192,6 @@ public class ProximitySensor implements AccelerometerListener.OrientationListene
// proximity sensor goes negative.
final boolean horizontal =
(mOrientation == AccelerometerListener.ORIENTATION_HORIZONTAL);
- Log.i(this, "horizontal: " + horizontal);
screenOnImmediately |= !mUiShowing && horizontal;
// We do not keep the screen off when dialpad is visible, we are horizontal, and
@@ -205,22 +202,29 @@ public class ProximitySensor implements AccelerometerListener.OrientationListene
Log.v(this, "screenonImmediately: ", screenOnImmediately);
+ Log.i(this, Objects.toStringHelper(this)
+ .add("keybrd", mIsHardKeyboardOpen ? 1 : 0)
+ .add("dpad", mDialpadVisible ? 1 : 0)
+ .add("offhook", mIsPhoneOffhook ? 1 : 0)
+ .add("aud", audioMode)
+ .add("hor", horizontal ? 1 : 0).toString());
+
if (mIsPhoneOffhook && !screenOnImmediately) {
- Log.i(this, "turning on proximity sensor");
+ final String logStr = "turning off proximity sensor: ";
// Phone is in use! Arrange for the screen to turn off
// automatically when the sensor detects a close object.
if (!mProximityWakeLock.isHeld()) {
- Log.d(this, "updateProximitySensorMode: acquiring...");
+ Log.i(this, logStr + "acquiring");
mProximityWakeLock.acquire();
} else {
- Log.v(this, "updateProximitySensorMode: lock already held.");
+ Log.i(this, logStr + "already acquired");
}
} else {
- Log.i(this, "turning off proximity sensor");
+ final String logStr = "turning off proximity sensor: ";
// Phone is either idle, or ringing. We don't want any
// special proximity sensor behavior in either case.
if (mProximityWakeLock.isHeld()) {
- Log.d(this, "updateProximitySensorMode: releasing...");
+ Log.i(this, logStr + "releasing");
// Wait until user has moved the phone away from his head if we are
// releasing due to the phone call ending.
// Qtherwise, turn screen on immediately
@@ -228,7 +232,7 @@ public class ProximitySensor implements AccelerometerListener.OrientationListene
(screenOnImmediately ? 0 : PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE);
mProximityWakeLock.release(flags);
} else {
- Log.v(this, "updateProximitySensorMode: lock already released.");
+ Log.i(this, logStr + "already released");
}
}
}
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index ecdf53238..c5b60fe82 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -48,7 +48,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
private final CallList mCallList;
private final NotificationManager mNotificationManager;
private boolean mIsShowingNotification = false;
- private InCallState mInCallState = InCallState.HIDDEN;
+ private InCallState mInCallState = InCallState.NO_CALLS;
private int mSavedIcon = 0;
private int mSavedContent = 0;
private Bitmap mSavedLargeIcon;