Prepare playback immediately, new BackgroundTaskService.
- Immediately we start the CallDetailActivity with a voicemail, do the
preparing of the media so we can see the duration.
- Use the duration text field to show "buffering..." until media source
is prepared.
- Do the preparing in the background, this fixes a strict mode
violation.
- If there's an error preparing, show an error message in that field.
- Add tests for both of the above cases.
BackgroundTaskService:
- Introduces new BackgroundTaskService, lightweight method for
submitting AsyncTask objects.
- Introduces BackgroundTask interface and simple AbstractBackgroundTask
abstract implementation.
- Adds BackgroundTaskService to ContactsApplication allowing Activity
objects to get hold of a regular background task service.
- Adds a MockBackgroundTaskService for use with injecting for test, so
that we can prevent or control processing of background tasks.
Other:
- Every time we resume the Activity, we were causing a new voicemail
fragment to be created. There was no bug tracking this, I just
noticed it because of occasionally flaky tests. Added a fix and test
to catch it again next time.
- Fixes missing tear down method in PeopleActivityTest.
Bug: 5115133
Bug: 5059965
Bug: 5114261
Bug: 5113695
Change-Id: Ia2469229fa756da8b3977231fbf23a9d3fb379ce
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 163069a..43e6fc7 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -19,6 +19,9 @@
import com.android.contacts.calllog.CallDetailHistoryAdapter;
import com.android.contacts.calllog.CallTypeHelper;
import com.android.contacts.calllog.PhoneNumberHelper;
+import com.android.contacts.util.AbstractBackgroundTask;
+import com.android.contacts.util.BackgroundTask;
+import com.android.contacts.util.BackgroundTaskService;
import com.android.contacts.voicemail.VoicemailPlaybackFragment;
import com.android.contacts.voicemail.VoicemailStatusHelper;
import com.android.contacts.voicemail.VoicemailStatusHelper.StatusMessage;
@@ -34,7 +37,6 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
@@ -87,6 +89,7 @@
private ImageView mMainActionView;
private ImageButton mMainActionPushLayerView;
private ImageView mContactBackgroundView;
+ private BackgroundTaskService mBackgroundTaskService;
private String mNumber = null;
private String mDefaultCountryIso;
@@ -144,6 +147,8 @@
setContentView(R.layout.call_detail);
+ mBackgroundTaskService = (BackgroundTaskService) getApplicationContext().getSystemService(
+ BackgroundTaskService.BACKGROUND_TASK_SERVICE);
mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
mResources = getResources();
@@ -164,13 +169,13 @@
mContactPhotoManager = ContactPhotoManager.getInstance(this);
getListView().setOnItemClickListener(this);
configureActionBar();
+ optionallyHandleVoicemail();
}
@Override
public void onResume() {
super.onResume();
updateData(getCallLogEntryUris());
- optionallyHandleVoicemail();
}
/**
@@ -210,15 +215,14 @@
}
private void markVoicemailAsRead(final Uri voicemailUri) {
- new AsyncTask<Void, Void, Void>() {
+ mBackgroundTaskService.submit(new AbstractBackgroundTask() {
@Override
- protected Void doInBackground(Void... params) {
+ public void doInBackground() {
ContentValues values = new ContentValues();
values.put(Voicemails.IS_READ, true);
getContentResolver().update(voicemailUri, values, null, null);
- return null;
}
- }.execute();
+ });
}
/**
@@ -662,12 +666,16 @@
}
callIds.append(ContentUris.parseId(callUri));
}
- runInBackgroundThenFinishActivity(new Runnable() {
+ mBackgroundTaskService.submit(new BackgroundTask() {
@Override
- public void run() {
+ public void doInBackground() {
getContentResolver().delete(Calls.CONTENT_URI_WITH_VOICEMAIL,
Calls._ID + " IN (" + callIds + ")", null);
}
+ @Override
+ public void onPostExecute() {
+ finish();
+ }
});
}
public void onMenuEditNumberBeforeCall(MenuItem menuItem) {
@@ -680,29 +688,16 @@
public void onMenuTrashVoicemail(MenuItem menuItem) {
final Uri voicemailUri = getVoicemailUri();
- runInBackgroundThenFinishActivity(new Runnable() {
+ mBackgroundTaskService.submit(new BackgroundTask() {
@Override
- public void run() {
+ public void doInBackground() {
getContentResolver().delete(voicemailUri, null, null);
}
- });
- }
-
- /**
- * Run a task in the background, and then finish this activity when the task is done.
- */
- private void runInBackgroundThenFinishActivity(final Runnable runnable) {
- new AsyncTask<Void, Void, Void>() {
@Override
- protected Void doInBackground(Void... params) {
- runnable.run();
- return null;
- }
- @Override
- protected void onPostExecute(Void result) {
+ public void onPostExecute() {
finish();
}
- }.execute();
+ });
}
private void configureActionBar() {
diff --git a/src/com/android/contacts/ContactsApplication.java b/src/com/android/contacts/ContactsApplication.java
index 1c8c080..1e791b6 100644
--- a/src/com/android/contacts/ContactsApplication.java
+++ b/src/com/android/contacts/ContactsApplication.java
@@ -18,6 +18,7 @@
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.test.InjectedServices;
+import com.android.contacts.util.BackgroundTaskService;
import com.google.common.annotations.VisibleForTesting;
import android.app.Application;
@@ -33,6 +34,7 @@
private static InjectedServices sInjectedServices;
private AccountTypeManager mAccountTypeManager;
private ContactPhotoManager mContactPhotoManager;
+ private BackgroundTaskService mBackgroundTaskService;
/**
* Overrides the system services with mocks for testing.
@@ -93,6 +95,13 @@
return mContactPhotoManager;
}
+ if (BackgroundTaskService.BACKGROUND_TASK_SERVICE.equals(name)) {
+ if (mBackgroundTaskService == null) {
+ mBackgroundTaskService = BackgroundTaskService.createBackgroundTaskService();
+ }
+ return mBackgroundTaskService;
+ }
+
return super.getSystemService(name);
}
diff --git a/src/com/android/contacts/util/AbstractBackgroundTask.java b/src/com/android/contacts/util/AbstractBackgroundTask.java
new file mode 100644
index 0000000..c492e7c
--- /dev/null
+++ b/src/com/android/contacts/util/AbstractBackgroundTask.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.util;
+
+import com.android.contacts.util.BackgroundTask;
+
+/**
+ * Base class you can use if you only want to override the {@link #doInBackground()} method.
+ */
+public abstract class AbstractBackgroundTask implements BackgroundTask {
+ @Override
+ public void onPostExecute() {
+ // No action necessary.
+ }
+}
diff --git a/src/com/android/contacts/util/BackgroundTask.java b/src/com/android/contacts/util/BackgroundTask.java
new file mode 100644
index 0000000..ba791fb
--- /dev/null
+++ b/src/com/android/contacts/util/BackgroundTask.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.util;
+
+/**
+ * Simple interface to improve the testability of code using AsyncTasks.
+ * <p>
+ * Provides a trivial replacement for no-arg versions of AsyncTask clients. We may extend this
+ * to add more functionality as we require.
+ * <p>
+ * The same memory-visibility guarantees are made here as are made for AsyncTask objects, namely
+ * that fields set in {@link #doInBackground()} are visible to {@link #onPostExecute()}.
+ */
+public interface BackgroundTask {
+ public void doInBackground();
+ public void onPostExecute();
+}
diff --git a/src/com/android/contacts/util/BackgroundTaskService.java b/src/com/android/contacts/util/BackgroundTaskService.java
new file mode 100644
index 0000000..00f9e3e
--- /dev/null
+++ b/src/com/android/contacts/util/BackgroundTaskService.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.util;
+
+import android.os.AsyncTask;
+
+/**
+ * Service used to submit tasks to run in the background.
+ * <p>
+ * BackgroundTaskService makes the same memory-visibility guarantees that AsyncTask which it
+ * emulates makes, namely that fields set in the {@link BackgroundTask#doInBackground()} method
+ * will be visible to the {@link BackgroundTask#onPostExecute()} method.
+ */
+public abstract class BackgroundTaskService {
+ public static final String BACKGROUND_TASK_SERVICE = BackgroundTaskService.class.getName();
+
+ public abstract void submit(BackgroundTask task);
+
+ public static BackgroundTaskService createBackgroundTaskService() {
+ return new AsyncTaskBackgroundTaskService();
+ }
+
+ private static final class AsyncTaskBackgroundTaskService extends BackgroundTaskService {
+ @Override
+ public void submit(final BackgroundTask task) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ task.doInBackground();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ task.onPostExecute();
+ }
+ }.execute();
+ }
+ }
+}
diff --git a/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java b/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java
index fcf99b6..0b30cd9 100644
--- a/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java
+++ b/src/com/android/contacts/voicemail/VoicemailPlaybackFragment.java
@@ -20,6 +20,7 @@
import static com.android.contacts.CallDetailActivity.EXTRA_VOICEMAIL_URI;
import com.android.contacts.R;
+import com.android.contacts.util.BackgroundTaskService;
import com.android.ex.variablespeed.MediaPlayerProxy;
import com.android.ex.variablespeed.VariableSpeed;
import com.google.common.base.Preconditions;
@@ -36,7 +37,6 @@
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
-import android.widget.Toast;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -97,10 +97,15 @@
boolean startPlayback = arguments.getBoolean(EXTRA_VOICEMAIL_START_PLAYBACK, false);
mPresenter = new VoicemailPlaybackPresenter(new PlaybackViewImpl(),
createMediaPlayer(mScheduledExecutorService), voicemailUri,
- mScheduledExecutorService, startPlayback);
+ mScheduledExecutorService, startPlayback, getBackgroundTaskService());
mPresenter.onCreate(savedInstanceState);
}
+ private BackgroundTaskService getBackgroundTaskService() {
+ return (BackgroundTaskService) getActivity().getApplicationContext().getSystemService(
+ BackgroundTaskService.BACKGROUND_TASK_SERVICE);
+ }
+
@Override
public void onSaveInstanceState(Bundle outState) {
mPresenter.onSaveInstanceState(outState);
@@ -213,6 +218,11 @@
}
@Override
+ public void setIsBuffering() {
+ mTextController.setPermanentText(getString(R.string.voicemail_buffering));
+ }
+
+ @Override
public int getDesiredClipPosition() {
return mPlaybackSeek.getProgress();
}
@@ -224,7 +234,7 @@
mStartStopButton.setEnabled(false);
mPlaybackSeek.setProgress(0);
mPlaybackSeek.setEnabled(false);
- Toast.makeText(getActivity(), R.string.voicemail_playback_error, Toast.LENGTH_SHORT);
+ mTextController.setPermanentText(getString(R.string.voicemail_playback_error));
Log.e(TAG, "Could not play voicemail", e);
}
diff --git a/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
index 6f3a625..e901fab 100644
--- a/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
@@ -19,6 +19,8 @@
import static android.util.MathUtils.constrain;
import com.android.contacts.R;
+import com.android.contacts.util.BackgroundTask;
+import com.android.contacts.util.BackgroundTaskService;
import com.android.ex.variablespeed.MediaPlayerProxy;
import com.android.ex.variablespeed.SingleThreadedMediaPlayerProxy;
@@ -58,6 +60,7 @@
void setStartStopListener(View.OnClickListener listener);
void setPositionSeekListener(SeekBar.OnSeekBarChangeListener listener);
void setSpeakerphoneListener(View.OnClickListener listener);
+ void setIsBuffering();
void setClipPosition(int clipPositionInMillis, int clipLengthInMillis);
int getDesiredClipPosition();
void playbackStarted();
@@ -124,18 +127,48 @@
private final Uri mVoicemailUri;
/** Start playing in onCreate iff this is true. */
private final boolean mStartPlayingImmediately;
+ /** Used to run background tasks that need to interact with the ui. */
+ private final BackgroundTaskService mBackgroundTaskService;
public VoicemailPlaybackPresenter(PlaybackView view, MediaPlayerProxy player,
Uri voicemailUri, ScheduledExecutorService executorService,
- boolean startPlayingImmediately) {
+ boolean startPlayingImmediately, BackgroundTaskService backgroundTaskService) {
mView = view;
mPlayer = player;
mVoicemailUri = voicemailUri;
mStartPlayingImmediately = startPlayingImmediately;
+ mBackgroundTaskService = backgroundTaskService;
mPositionUpdater = new PositionUpdater(executorService, SLIDER_UPDATE_PERIOD_MILLIS);
}
public void onCreate(Bundle bundle) {
+ mView.setIsBuffering();
+ mBackgroundTaskService.submit(new BackgroundTask() {
+ private Exception mException;
+
+ @Override
+ public void doInBackground() {
+ try {
+ mPlayer.reset();
+ mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
+ mPlayer.prepare();
+ } catch (IOException e) {
+ mException = e;
+ }
+ }
+
+ @Override
+ public void onPostExecute() {
+ if (mException == null) {
+ postSuccessfulPrepareActions();
+ } else {
+ mView.playbackError(mException);
+ }
+ }
+ });
+ }
+
+ private void postSuccessfulPrepareActions() {
mView.setPositionSeekListener(new PlaybackPositionListener());
mView.setStartStopListener(new StartStopButtonListener());
mView.setSpeakerphoneListener(new SpeakerphoneListener());
@@ -144,7 +177,7 @@
mView.setSpeakerPhoneOn(mView.isSpeakerPhoneOn());
mView.setRateDecreaseButtonListener(createRateDecreaseListener());
mView.setRateIncreaseButtonListener(createRateIncreaseListener());
- mView.setClipPosition(0, 0);
+ mView.setClipPosition(0, mPlayer.getDuration());
mView.playbackStopped();
if (mStartPlayingImmediately) {
resetPrepareStartPlaying(0);