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
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index 7f27133..607e966 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -48,6 +48,7 @@
@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 @@
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 8224d3e..8700875 100644
--- a/InCallUI/src/com/android/incallui/AudioModeProvider.java
+++ b/InCallUI/src/com/android/incallui/AudioModeProvider.java
@@ -28,13 +28,14 @@
*/
/* 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 ae207f3..6c2ba21 100644
--- a/InCallUI/src/com/android/incallui/BaseFragment.java
+++ b/InCallUI/src/com/android/incallui/BaseFragment.java
@@ -52,6 +52,6 @@
@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 17273a8..b2884e6 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -30,7 +30,6 @@
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 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 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 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 @@
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 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 ee58706..801e749 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -49,7 +49,6 @@
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 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 @@
// 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 @@
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 b731355..b2b76f2 100644
--- a/InCallUI/src/com/android/incallui/CallHandlerService.java
+++ b/InCallUI/src/com/android/incallui/CallHandlerService.java
@@ -56,20 +56,21 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 6cae6b0..fb9f99c 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -58,14 +58,17 @@
// 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 @@
protected void onDestroy() {
Log.d(this, "onDestroy()... this = " + this);
- tearDownPresenters();
+ InCallPresenter.getInstance().setActivity(null);
super.onDestroy();
}
@@ -293,36 +296,6 @@
.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 cc4cee2..1e2f675 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -51,7 +51,7 @@
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 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 @@
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 @@
* the tear-down process.
*/
public void setActivity(InCallActivity inCallActivity) {
- mInCallActivity = inCallActivity;
+ boolean updateListeners = false;
- if (mInCallActivity != null) {
- Log.i(this, "UI Initialized");
+ 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 @@
*/
@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 @@
* 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 @@
* 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 @@
// 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 @@
// 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 @@
* 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 enum InCallState {
// InCall Screen is off and there are no calls
- HIDDEN,
+ NO_CALLS,
// Incoming-call screen is up
INCOMING,
@@ -413,10 +458,6 @@
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 b4962ae..d2f2d36 100644
--- a/InCallUI/src/com/android/incallui/Presenter.java
+++ b/InCallUI/src/com/android/incallui/Presenter.java
@@ -35,10 +35,18 @@
/**
* 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 4befb2b..c28a69e 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.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 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 @@
* 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 @@
// 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 @@
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 @@
(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 ecdf532..c5b60fe 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -48,7 +48,7 @@
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;