From c70343becd3734fd380e0d20f05b7605e6233d43 Mon Sep 17 00:00:00 2001 From: Sarmad Hashmi Date: Thu, 10 Mar 2016 20:22:43 -0800 Subject: Refactor voicemail archive code. +Created new class for all AsyncTasks and listener interfaces. +Separated the archive code into separate methods +Added 7 tests BUG=22797391 Change-Id: I789125328bf079846e34c5fd57fd0d1c67dc6266 --- .../dialer/voicemail/VoicemailAsyncTaskUtil.java | 346 +++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java (limited to 'src/com') diff --git a/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java b/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java new file mode 100644 index 000000000..7abf9a72c --- /dev/null +++ b/src/com/android/dialer/voicemail/VoicemailAsyncTaskUtil.java @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2016 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.dialer.voicemail; + +import com.android.contacts.common.testing.NeededForTesting; +import com.android.dialer.calllog.CallLogQuery; +import com.android.dialer.database.VoicemailArchiveContract; +import com.android.dialer.util.AsyncTaskExecutor; +import com.android.dialer.util.AsyncTaskExecutors; +import com.google.common.base.Preconditions; +import com.google.common.io.ByteStreams; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.CallLog; +import android.provider.VoicemailContract; +import android.util.Log; +import com.android.common.io.MoreCloseables; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.annotation.Nullable; + +/** + * Class containing asynchronous tasks for voicemails. + */ +@NeededForTesting +public class VoicemailAsyncTaskUtil { + private static final String TAG = "VoicemailAsyncTaskUtil"; + + /** The enumeration of {@link AsyncTask} objects we use in this class. */ + public enum Tasks { + GET_VOICEMAIL_FILE_PATH, + SET_VOICEMAIL_ARCHIVE_STATUS, + ARCHIVE_VOICEMAIL_CONTENT + } + + @NeededForTesting + public interface OnArchiveVoicemailListener { + /** + * Called after the voicemail has been archived. + * + * @param archivedVoicemailUri the URI of the archived voicemail + */ + void onArchiveVoicemail(@Nullable Uri archivedVoicemailUri); + } + + @NeededForTesting + public interface OnSetVoicemailArchiveStatusListener { + /** + * Called after the voicemail archived_by_user column is updated. + * + * @param success whether the update was successful or not + */ + void onSetVoicemailArchiveStatus(boolean success); + } + + @NeededForTesting + public interface OnGetArchivedVoicemailFilePathListener { + /** + * Called after the voicemail file path is obtained. + * + * @param filePath the file path of the archived voicemail + */ + void onGetArchivedVoicemailFilePath(@Nullable String filePath); + } + + private final ContentResolver mResolver; + private final AsyncTaskExecutor mAsyncTaskExecutor; + + @NeededForTesting + public VoicemailAsyncTaskUtil(ContentResolver contentResolver) { + mResolver = Preconditions.checkNotNull(contentResolver); + mAsyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor(); + } + + /** + * Returns the archived voicemail file path. + */ + @NeededForTesting + public void getVoicemailFilePath( + final OnGetArchivedVoicemailFilePathListener listener, + final Uri voicemailUri) { + Preconditions.checkNotNull(listener); + Preconditions.checkNotNull(voicemailUri); + mAsyncTaskExecutor.submit(Tasks.GET_VOICEMAIL_FILE_PATH, + new AsyncTask() { + @Nullable + @Override + protected String doInBackground(Void... params) { + try (Cursor cursor = mResolver.query(voicemailUri, + new String[]{VoicemailArchiveContract.VoicemailArchive._DATA}, + null, null, null)) { + if (hasContent(cursor)) { + return cursor.getString(cursor.getColumnIndex( + VoicemailArchiveContract.VoicemailArchive._DATA)); + } + } + return null; + } + + @Override + protected void onPostExecute(String filePath) { + listener.onGetArchivedVoicemailFilePath(filePath); + } + }); + } + + /** + * Updates the archived_by_user flag of the archived voicemail. + */ + @NeededForTesting + public void setVoicemailArchiveStatus( + final OnSetVoicemailArchiveStatusListener listener, + final Uri voicemailUri, + final boolean archivedByUser) { + Preconditions.checkNotNull(listener); + Preconditions.checkNotNull(voicemailUri); + mAsyncTaskExecutor.submit(Tasks.SET_VOICEMAIL_ARCHIVE_STATUS, + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... params) { + ContentValues values = new ContentValues(1); + values.put(VoicemailArchiveContract.VoicemailArchive.ARCHIVED, + archivedByUser); + return mResolver.update(voicemailUri, values, null, null) > 0; + } + + @Override + protected void onPostExecute(Boolean success) { + listener.onSetVoicemailArchiveStatus(success); + } + }); + } + + /** + * Checks if a voicemail has already been archived, if so, return the previously archived URI. + * Otherwise, copy the voicemail information to the local dialer database. If archive was + * successful, archived voicemail URI is returned to listener, otherwise null. + */ + @NeededForTesting + public void archiveVoicemailContent( + final OnArchiveVoicemailListener listener, + final Uri voicemailUri) { + Preconditions.checkNotNull(listener); + Preconditions.checkNotNull(voicemailUri); + mAsyncTaskExecutor.submit(Tasks.ARCHIVE_VOICEMAIL_CONTENT, + new AsyncTask() { + @Nullable + @Override + protected Uri doInBackground(Void... params) { + Uri archivedVoicemailUri = getArchivedVoicemailUri(voicemailUri); + + // If previously archived, return uri, otherwise archive everything. + if (archivedVoicemailUri != null) { + return archivedVoicemailUri; + } + + // Combine call log and voicemail content info. + ContentValues values = getVoicemailContentValues(voicemailUri); + if (values == null) { + return null; + } + + Uri insertedVoicemailUri = mResolver.insert( + VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, values); + if (insertedVoicemailUri == null) { + return null; + } + + // Copy voicemail content to a new file. + boolean copiedFile = false; + try (InputStream inputStream = mResolver.openInputStream(voicemailUri); + OutputStream outputStream = + mResolver.openOutputStream(insertedVoicemailUri)) { + if (inputStream != null && outputStream != null) { + ByteStreams.copy(inputStream, outputStream); + copiedFile = true; + return insertedVoicemailUri; + } + } catch (IOException e) { + Log.w(TAG, "Failed to copy voicemail content to new file: " + + e.toString()); + } finally { + if (!copiedFile) { + // Roll back insert if the voicemail content was not copied. + mResolver.delete(insertedVoicemailUri, null, null); + } + } + return null; + } + + @Override + protected void onPostExecute(Uri archivedVoicemailUri) { + listener.onArchiveVoicemail(archivedVoicemailUri); + } + }); + } + + /** + * Helper method to get the archived URI of a voicemail. + * + * @param voicemailUri a {@link android.provider.VoicemailContract.Voicemails#CONTENT_URI} URI. + * @return the URI of the archived voicemail or {@code null} + */ + @Nullable + private Uri getArchivedVoicemailUri(Uri voicemailUri) { + try (Cursor cursor = getArchiveExistsCursor(voicemailUri)) { + if (hasContent(cursor)) { + return VoicemailArchiveContract.VoicemailArchive + .buildWithId(cursor.getInt(cursor.getColumnIndex( + VoicemailArchiveContract.VoicemailArchive._ID))); + } + } + return null; + } + + /** + * Helper method to make a copy of all the values needed to display a voicemail. + * + * @param voicemailUri a {@link VoicemailContract.Voicemails#CONTENT_URI} URI. + * @return the combined call log and voicemail values for the given URI, or {@code null} + */ + @Nullable + private ContentValues getVoicemailContentValues(Uri voicemailUri) { + try (Cursor callLogInfo = getCallLogInfoCursor(voicemailUri); + Cursor contentInfo = getContentInfoCursor(voicemailUri)) { + + if (hasContent(callLogInfo) && hasContent(contentInfo)) { + // Create values to insert into database. + ContentValues values = new ContentValues(); + + // Insert voicemail call log info. + values.put(VoicemailArchiveContract.VoicemailArchive.COUNTRY_ISO, + callLogInfo.getString(CallLogQuery.COUNTRY_ISO)); + values.put(VoicemailArchiveContract.VoicemailArchive.GEOCODED_LOCATION, + callLogInfo.getString(CallLogQuery.GEOCODED_LOCATION)); + values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NAME, + callLogInfo.getString(CallLogQuery.CACHED_NAME)); + values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_TYPE, + callLogInfo.getInt(CallLogQuery.CACHED_NUMBER_TYPE)); + values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_LABEL, + callLogInfo.getString(CallLogQuery.CACHED_NUMBER_LABEL)); + values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_LOOKUP_URI, + callLogInfo.getString(CallLogQuery.CACHED_LOOKUP_URI)); + values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_MATCHED_NUMBER, + callLogInfo.getString(CallLogQuery.CACHED_MATCHED_NUMBER)); + values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NORMALIZED_NUMBER, + callLogInfo.getString(CallLogQuery.CACHED_NORMALIZED_NUMBER)); + values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_FORMATTED_NUMBER, + callLogInfo.getString(CallLogQuery.CACHED_FORMATTED_NUMBER)); + values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER_PRESENTATION, + callLogInfo.getInt(CallLogQuery.NUMBER_PRESENTATION)); + values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_COMPONENT_NAME, + callLogInfo.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME)); + values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_ID, + callLogInfo.getString(CallLogQuery.ACCOUNT_ID)); + values.put(VoicemailArchiveContract.VoicemailArchive.FEATURES, + callLogInfo.getInt(CallLogQuery.FEATURES)); + values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_PHOTO_URI, + callLogInfo.getString(CallLogQuery.CACHED_PHOTO_URI)); + + // Insert voicemail content info. + values.put(VoicemailArchiveContract.VoicemailArchive.SERVER_ID, + contentInfo.getInt(contentInfo.getColumnIndex( + VoicemailContract.Voicemails._ID))); + values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER, + contentInfo.getString(contentInfo.getColumnIndex( + VoicemailContract.Voicemails.NUMBER))); + values.put(VoicemailArchiveContract.VoicemailArchive.DATE, + contentInfo.getLong(contentInfo.getColumnIndex( + VoicemailContract.Voicemails.DATE))); + values.put(VoicemailArchiveContract.VoicemailArchive.DURATION, + contentInfo.getLong(contentInfo.getColumnIndex( + VoicemailContract.Voicemails.DURATION))); + values.put(VoicemailArchiveContract.VoicemailArchive.MIME_TYPE, + contentInfo.getString(contentInfo.getColumnIndex( + VoicemailContract.Voicemails.MIME_TYPE))); + values.put(VoicemailArchiveContract.VoicemailArchive.TRANSCRIPTION, + contentInfo.getString(contentInfo.getColumnIndex( + VoicemailContract.Voicemails.TRANSCRIPTION))); + + // Achived is false by default because it is updated after insertion. + values.put(VoicemailArchiveContract.VoicemailArchive.ARCHIVED, false); + + return values; + } + } + return null; + } + + private boolean hasContent(@Nullable Cursor cursor) { + return cursor != null && cursor.moveToFirst(); + } + + @Nullable + private Cursor getCallLogInfoCursor(Uri voicemailUri) { + return mResolver.query( + ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, + ContentUris.parseId(voicemailUri)), + CallLogQuery._PROJECTION, null, null, null); + } + + @Nullable + private Cursor getContentInfoCursor(Uri voicemailUri) { + return mResolver.query(voicemailUri, + new String[] { + VoicemailContract.Voicemails._ID, + VoicemailContract.Voicemails.NUMBER, + VoicemailContract.Voicemails.DATE, + VoicemailContract.Voicemails.DURATION, + VoicemailContract.Voicemails.MIME_TYPE, + VoicemailContract.Voicemails.TRANSCRIPTION, + }, null, null, null); + } + + @Nullable + private Cursor getArchiveExistsCursor(Uri voicemailUri) { + return mResolver.query(VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, + new String[] {VoicemailArchiveContract.VoicemailArchive._ID}, + VoicemailArchiveContract.VoicemailArchive.SERVER_ID + "=" + + ContentUris.parseId(voicemailUri), + null, + null); + } +} -- cgit v1.2.3 From 25cfc81d829ccd7e0774862451089a4925adf932 Mon Sep 17 00:00:00 2001 From: Sarmad Hashmi Date: Fri, 11 Mar 2016 21:23:24 -0800 Subject: Add share voicemail functionality. +Send intent to other applications with temporary file permission granted +Add voicemails folder to FileProvider paths +When creating file in VoicemailArchiveProvider, append extension to file name +Check voicemail content exists before archiving it +Refactor voicemail code +Add tests for checking file extension +Add tests for archiving and sharing vociemails +Archive tab should not archive on share button press BUG=22798485 Change-Id: Id8068aba92f7f1ca58532dca6c405cb2908841a3 --- res/xml/file_paths.xml | 4 +- .../dialer/database/VoicemailArchiveProvider.java | 7 +- .../VoicemailArchivePlaybackPresenter.java | 17 +- .../dialer/voicemail/VoicemailPlaybackLayout.java | 12 + .../voicemail/VoicemailPlaybackPresenter.java | 247 +++++++-------------- .../database/VoicemailArchiveProviderTest.java | 42 +++- .../VoicemailActivityInstrumentationTestCase2.java | 17 +- .../dialer/voicemail/VoicemailArchiveTest.java | 6 +- .../dialer/voicemail/VoicemailPlaybackTest.java | 92 +++++++- 9 files changed, 267 insertions(+), 177 deletions(-) (limited to 'src/com') diff --git a/res/xml/file_paths.xml b/res/xml/file_paths.xml index 45caed283..365a60318 100644 --- a/res/xml/file_paths.xml +++ b/res/xml/file_paths.xml @@ -16,5 +16,7 @@ - + + + diff --git a/src/com/android/dialer/database/VoicemailArchiveProvider.java b/src/com/android/dialer/database/VoicemailArchiveProvider.java index 79b7a7630..b3306bc4c 100644 --- a/src/com/android/dialer/database/VoicemailArchiveProvider.java +++ b/src/com/android/dialer/database/VoicemailArchiveProvider.java @@ -28,6 +28,7 @@ import android.net.Uri; import android.os.ParcelFileDescriptor; import android.support.annotation.Nullable; import android.text.TextUtils; +import android.webkit.MimeTypeMap; import com.android.dialerbind.DatabaseHelperManager; import com.google.common.annotations.VisibleForTesting; @@ -119,7 +120,11 @@ public class VoicemailArchiveProvider extends ContentProvider { // Create new file only if path is not provided to one if (!values.containsKey(VoicemailArchiveContract.VoicemailArchive._DATA)) { - File voicemailFile = new File(directory, Long.toString(id)); + String fileExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType( + values.getAsString(VoicemailArchiveContract.VoicemailArchive.MIME_TYPE)); + File voicemailFile = new File(directory, + TextUtils.isEmpty(fileExtension) ? Long.toString(id) : + id + "." + fileExtension); values.put(VoicemailArchiveContract.VoicemailArchive._DATA, voicemailFile.getPath()); } update(newUri, values, null, null); diff --git a/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java index 050b8ac62..5f73d1689 100644 --- a/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java +++ b/src/com/android/dialer/voicemail/VoicemailArchivePlaybackPresenter.java @@ -62,7 +62,6 @@ public class VoicemailArchivePlaybackPresenter extends VoicemailPlaybackPresente } } catch (FileNotFoundException e) { Log.d(TAG, "Voicemail file not found for " + mVoicemailUri); - handleError(e); } return false; } @@ -75,11 +74,17 @@ public class VoicemailArchivePlaybackPresenter extends VoicemailPlaybackPresente } @Override - protected boolean requestContent(int code) { - if (mContext == null || mVoicemailUri == null) { - return false; + protected void startArchiveVoicemailTask(final Uri voicemailUri, final boolean archivedByUser) { + // If a user wants to share an archived voicemail, no need for archiving, just go straight + // to share intent. + if (!archivedByUser) { + sendShareIntent(voicemailUri); } - prepareContent(); - return true; + } + + @Override + protected boolean requestContent(int code) { + handleError(new FileNotFoundException("Voicemail archive file does not exist")); + return false; // No way for archive tab to request content } } diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java index 8c869d183..e5efbeca3 100644 --- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java +++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java @@ -258,6 +258,17 @@ public class VoicemailPlaybackLayout extends LinearLayout } }; + private final View.OnClickListener mShareButtonListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mPresenter == null || isArchiving(mVoicemailUri)) { + return; + } + disableUiElements(); + mPresenter.archiveContent(mVoicemailUri, false); + } + }; + private Context mContext; private VoicemailPlaybackPresenter mPresenter; private Uri mVoicemailUri; @@ -339,6 +350,7 @@ public class VoicemailPlaybackLayout extends LinearLayout mPlaybackSpeakerphone.setOnClickListener(mSpeakerphoneListener); mDeleteButton.setOnClickListener(mDeleteButtonListener); mArchiveButton.setOnClickListener(mArchiveButtonListener); + mShareButton.setOnClickListener(mShareButtonListener); mPositionText.setText(formatAsMinutesAndSeconds(0)); mTotalDurationText.setText(formatAsMinutesAndSeconds(0)); diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java index 3151a5ea5..5924fb453 100644 --- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java +++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java @@ -19,11 +19,8 @@ package com.android.dialer.voicemail; import com.google.common.annotations.VisibleForTesting; import android.app.Activity; -import android.content.ContentUris; -import android.content.ContentValues; import android.content.Context; import android.content.ContentResolver; -import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.database.Cursor; @@ -33,24 +30,19 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; -import android.provider.CallLog; import android.provider.VoicemailContract; -import android.support.annotation.Nullable; +import android.support.v4.content.FileProvider; import android.util.Log; import android.view.WindowManager.LayoutParams; +import com.android.dialer.R; import com.android.dialer.calllog.CallLogAsyncTaskUtil; -import com.android.dialer.calllog.CallLogQuery; -import com.android.dialer.database.VoicemailArchiveContract; import com.android.dialer.util.AsyncTaskExecutor; import com.android.dialer.util.AsyncTaskExecutors; import com.android.common.io.MoreCloseables; -import com.android.dialer.util.TelecomUtil; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.io.ByteStreams; + +import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; @@ -140,6 +132,7 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene VoicemailPlaybackPresenter.class.getName() + ".IS_SPEAKER_PHONE_ON"; public static final int PLAYBACK_REQUEST = 0; public static final int ARCHIVE_REQUEST = 1; + public static final int SHARE_REQUEST = 2; /** * The most recently cached duration. We cache this since we don't want to keep requesting it @@ -181,6 +174,7 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene private VoicemailAudioManager mVoicemailAudioManager; private OnVoicemailDeletedListener mOnVoicemailDeletedListener; + private final VoicemailAsyncTaskUtil mVoicemailAsyncTaskUtil; /** * Obtain singleton instance of this class. Use a single instance to provide a consistent @@ -208,6 +202,7 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene Context context = activity.getApplicationContext(); mAsyncTaskExecutor = AsyncTaskExecutors.createAsyncTaskExecutor(); mVoicemailAudioManager = new VoicemailAudioManager(context, this); + mVoicemailAsyncTaskUtil = new VoicemailAsyncTaskUtil(context.getContentResolver()); PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) { @@ -493,7 +488,6 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene private final Handler mFetchResultHandler; private final Uri mVoicemailUri; private final int mRequestCode; - private Uri mArchivedVoicemailUri; public FetchResultHandler(Handler handler, Uri uri, int code) { super(handler); @@ -515,11 +509,7 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene if (mIsWaitingForResult.getAndSet(false) && mContext != null) { mContext.getContentResolver().unregisterContentObserver(this); if (mView != null) { - if (mRequestCode == ARCHIVE_REQUEST) { - notifyUiOfArchiveResult(mVoicemailUri, false); - } else { - mView.setFetchContentTimeout(); - } + mView.setFetchContentTimeout(); } } } @@ -538,13 +528,7 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene @Override public Boolean doInBackground(Void... params) { - boolean hasContent = queryHasContent(mVoicemailUri); - if (hasContent && mRequestCode == ARCHIVE_REQUEST) { - mArchivedVoicemailUri = - performArchiveVoicemailOnBackgroundThread(mVoicemailUri, true); - return mArchivedVoicemailUri != null; - } - return hasContent; + return queryHasContent(mVoicemailUri); } @Override @@ -552,11 +536,11 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene if (hasContent && mContext != null && mIsWaitingForResult.getAndSet(false)) { mContext.getContentResolver().unregisterContentObserver( FetchResultHandler.this); - switch (mRequestCode) { - case ARCHIVE_REQUEST: - notifyUiOfArchiveResult(mVoicemailUri, true); - default: - prepareContent(); + prepareContent(); + if (mRequestCode == ARCHIVE_REQUEST) { + startArchiveVoicemailTask(mVoicemailUri, true /* archivedByUser */); + } else if (mRequestCode == SHARE_REQUEST) { + startArchiveVoicemailTask(mVoicemailUri, false /* archivedByUser */); } } } @@ -922,148 +906,89 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene * If voicemail has already been downloaded, go straight to archiving. Otherwise, request * the voicemail content first. */ - public void archiveContent(Uri voicemailUri, boolean archivedByUser) { - if (!mIsPrepared) { - requestContent(ARCHIVE_REQUEST); - } else { - startArchiveVoicemailTask(voicemailUri, archivedByUser); - } + public void archiveContent(final Uri voicemailUri, final boolean archivedByUser) { + checkForContent(new OnContentCheckedListener() { + @Override + public void onContentChecked(boolean hasContent) { + if (!hasContent) { + requestContent(archivedByUser ? ARCHIVE_REQUEST : SHARE_REQUEST); + } else { + startArchiveVoicemailTask(voicemailUri, archivedByUser); + } + } + }); } /** * Asynchronous task used to archive a voicemail given its uri. */ - private void startArchiveVoicemailTask(final Uri voicemailUri, final boolean archivedByUser) { - mAsyncTaskExecutor.submit(Tasks.ARCHIVE_VOICEMAIL, new AsyncTask() { - @Override - public Uri doInBackground(Void... params) { - return performArchiveVoicemailOnBackgroundThread(voicemailUri, archivedByUser); - } + protected void startArchiveVoicemailTask(final Uri voicemailUri, final boolean archivedByUser) { + mVoicemailAsyncTaskUtil.archiveVoicemailContent( + new VoicemailAsyncTaskUtil.OnArchiveVoicemailListener() { + @Override + public void onArchiveVoicemail(final Uri archivedVoicemailUri) { + if (archivedVoicemailUri == null) { + notifyUiOfArchiveResult(voicemailUri, false); + return; + } - @Override - public void onPostExecute(Uri archivedVoicemailUri) { - notifyUiOfArchiveResult(voicemailUri, archivedVoicemailUri != null); - } - }); + if (archivedByUser) { + setArchivedVoicemailStatusAndUpdateUI(voicemailUri, + archivedVoicemailUri, true); + } else { + sendShareIntent(archivedVoicemailUri); + } + } + }, voicemailUri); } /** - * Copy the voicemail information to the local dialer database, and copy - * the voicemail content to a local file in the dialer application's - * internal storage (voicemails directory). - * - * @param voicemailUri the uri of the voicemail to archive - * @return If archive was successful, archived voicemail URI, otherwise null. + * Sends the intent for sharing the voicemail file. */ - private Uri performArchiveVoicemailOnBackgroundThread(Uri voicemailUri, - boolean archivedByUser) { - Cursor callLogInfo = mContext.getContentResolver().query( - ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, - ContentUris.parseId(mVoicemailUri)), - CallLogQuery._PROJECTION, null, null, null); - Cursor contentInfo = mContext.getContentResolver().query( - voicemailUri, null, null, null, null); - - if (callLogInfo == null || contentInfo == null) { - return null; - } - - callLogInfo.moveToFirst(); - contentInfo.moveToFirst(); - - // Create values to insert into database - ContentValues values = new ContentValues(); - values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER, - contentInfo.getString(contentInfo.getColumnIndex( - VoicemailContract.Voicemails.NUMBER))); - - values.put(VoicemailArchiveContract.VoicemailArchive.DATE, - contentInfo.getLong(contentInfo.getColumnIndex( - VoicemailContract.Voicemails.DATE))); - - values.put(VoicemailArchiveContract.VoicemailArchive.DURATION, - contentInfo.getLong(contentInfo.getColumnIndex( - VoicemailContract.Voicemails.DURATION))); - - values.put(VoicemailArchiveContract.VoicemailArchive.MIME_TYPE, - contentInfo.getString(contentInfo.getColumnIndex( - VoicemailContract.Voicemails.MIME_TYPE))); - - values.put(VoicemailArchiveContract.VoicemailArchive.COUNTRY_ISO, - callLogInfo.getString(CallLogQuery.COUNTRY_ISO)); - - values.put(VoicemailArchiveContract.VoicemailArchive.GEOCODED_LOCATION, - callLogInfo.getString(CallLogQuery.GEOCODED_LOCATION)); - - values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NAME, - callLogInfo.getString(CallLogQuery.CACHED_NAME)); - - values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_TYPE, - callLogInfo.getInt(CallLogQuery.CACHED_NUMBER_TYPE)); - - values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NUMBER_LABEL, - callLogInfo.getString(CallLogQuery.CACHED_NUMBER_LABEL)); - - values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_LOOKUP_URI, - callLogInfo.getString(CallLogQuery.CACHED_LOOKUP_URI)); - - values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_MATCHED_NUMBER, - callLogInfo.getString(CallLogQuery.CACHED_MATCHED_NUMBER)); - - values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_NORMALIZED_NUMBER, - callLogInfo.getString(CallLogQuery.CACHED_NORMALIZED_NUMBER)); - - values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_FORMATTED_NUMBER, - callLogInfo.getString(CallLogQuery.CACHED_FORMATTED_NUMBER)); - - values.put(VoicemailArchiveContract.VoicemailArchive.ARCHIVED, archivedByUser); - - values.put(VoicemailArchiveContract.VoicemailArchive.NUMBER_PRESENTATION, - callLogInfo.getInt(CallLogQuery.NUMBER_PRESENTATION)); - - values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_COMPONENT_NAME, - callLogInfo.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME)); - - values.put(VoicemailArchiveContract.VoicemailArchive.ACCOUNT_ID, - callLogInfo.getString(CallLogQuery.ACCOUNT_ID)); - - values.put(VoicemailArchiveContract.VoicemailArchive.FEATURES, - callLogInfo.getInt(CallLogQuery.FEATURES)); - - values.put(VoicemailArchiveContract.VoicemailArchive.SERVER_ID, - contentInfo.getInt(contentInfo.getColumnIndex( - VoicemailContract.Voicemails._ID))); - - values.put(VoicemailArchiveContract.VoicemailArchive.TRANSCRIPTION, - contentInfo.getString(contentInfo.getColumnIndex( - VoicemailContract.Voicemails.TRANSCRIPTION))); - - values.put(VoicemailArchiveContract.VoicemailArchive.CACHED_PHOTO_URI, - callLogInfo.getLong(CallLogQuery.CACHED_PHOTO_URI)); - - callLogInfo.close(); - contentInfo.close(); + protected void sendShareIntent(final Uri voicemailUri) { + mVoicemailAsyncTaskUtil.getVoicemailFilePath( + new VoicemailAsyncTaskUtil.OnGetArchivedVoicemailFilePathListener() { + @Override + public void onGetArchivedVoicemailFilePath(String filePath) { + mView.enableUiElements(); + if (filePath == null) { + mView.setFetchContentTimeout(); + return; + } + Uri voicemailFileUri = FileProvider.getUriForFile( + mContext, + mContext.getString(R.string.contacts_file_provider_authority), + new File(filePath)); + mContext.startActivity(Intent.createChooser( + getShareIntent(voicemailFileUri), + mContext.getResources().getText( + R.string.call_log_share_voicemail))); + } + }, voicemailUri); + } + + /** Sets archived_by_user field to the given boolean and updates the URI. */ + private void setArchivedVoicemailStatusAndUpdateUI( + final Uri voicemailUri, + final Uri archivedVoicemailUri, + boolean status) { + mVoicemailAsyncTaskUtil.setVoicemailArchiveStatus( + new VoicemailAsyncTaskUtil.OnSetVoicemailArchiveStatusListener() { + @Override + public void onSetVoicemailArchiveStatus(boolean success) { + notifyUiOfArchiveResult(voicemailUri, success); + } + }, archivedVoicemailUri, status); + } - // Insert info into dialer database - Uri archivedVoicemailUri = mContext.getContentResolver().insert( - VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, values); - try { - // Copy voicemail content to a local file - InputStream inputStream = mContext.getContentResolver() - .openInputStream(voicemailUri); - OutputStream outputStream = mContext.getContentResolver() - .openOutputStream(archivedVoicemailUri); - - ByteStreams.copy(inputStream, outputStream); - inputStream.close(); - outputStream.close(); - } catch (IOException e) { - // Roll back insert if new file creation failed - mContext.getContentResolver().delete(archivedVoicemailUri, null, null); - Log.w(TAG, "Failed to copy voicemail content to temporary file"); - return null; - } - return archivedVoicemailUri; + private Intent getShareIntent(Uri voicemailFileUri) { + Intent shareIntent = new Intent(); + shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.putExtra(Intent.EXTRA_STREAM, voicemailFileUri); + shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + shareIntent.setType(mContext.getContentResolver() + .getType(voicemailFileUri)); + return shareIntent; } @VisibleForTesting diff --git a/tests/src/com/android/dialer/database/VoicemailArchiveProviderTest.java b/tests/src/com/android/dialer/database/VoicemailArchiveProviderTest.java index cec7e050d..abc2dc5b0 100644 --- a/tests/src/com/android/dialer/database/VoicemailArchiveProviderTest.java +++ b/tests/src/com/android/dialer/database/VoicemailArchiveProviderTest.java @@ -79,6 +79,33 @@ public class VoicemailArchiveProviderTest extends assertTrue(doesFileExist()); } + public void testQuery_createFileMimeTypeIsAMR() throws Exception { + insertVoicemailWithMimeType("audio/amr"); + assertTrue(doesRowExist()); + assertFalse(doesFileExist()); + createFile(); + assertTrue(doesFileExist()); + assertEquals("amr", getFileExtension(getFilePath())); + } + + public void testQuery_createFileMimeTypeIsMP3() throws Exception { + insertVoicemailWithMimeType("audio/mpeg"); + assertTrue(doesRowExist()); + assertFalse(doesFileExist()); + createFile(); + assertTrue(doesFileExist()); + assertEquals("mp3", getFileExtension(getFilePath())); + } + + public void testQuery_createFileMimeTypeNotExists() throws Exception { + insertVoicemailWithMimeType(TEST_STRING); + assertTrue(doesRowExist()); + assertFalse(doesFileExist()); + createFile(); + assertTrue(doesFileExist()); + assertEquals("", getFileExtension(getFilePath())); + } + public void testQuery() { insertVoicemail(); updateCursor(); @@ -179,6 +206,13 @@ public class VoicemailArchiveProviderTest extends return voicemailFile.exists(); } + private static String getFileExtension(String filePath) { + File file = new File(filePath); + String fileName = file.getName(); + int index = fileName.lastIndexOf("."); + return index > 0 ? fileName.substring(index + 1) : ""; + } + private void assertCursorCount(int count) { assertEquals(count, mCursor.getCount()); } @@ -196,6 +230,12 @@ public class VoicemailArchiveProviderTest extends mVoicemailUri = mResolver.insert(VoicemailArchive.CONTENT_URI, getTestValues()); } + private void insertVoicemailWithMimeType(String mimeType) { + ContentValues values = getTestValues(); + values.put(VoicemailArchive.MIME_TYPE, mimeType); + mVoicemailUri = mResolver.insert(VoicemailArchive.CONTENT_URI, values); + } + private void updateCursor() { mCursor = mResolver.query(mVoicemailUri, null, null, null, null); assertEquals(true, mCursor.getCount() > 0); @@ -226,7 +266,7 @@ public class VoicemailArchiveProviderTest extends return mCursor.getString(mCursor.getColumnIndex(VoicemailArchive._DATA)); } - private ContentValues getTestValues() { + private static ContentValues getTestValues() { ContentValues values = new ContentValues(); values.put(VoicemailArchive.NUMBER, TEST_NUMBER); values.put(VoicemailArchive.MIME_TYPE, TEST_MIME_TYPE); diff --git a/tests/src/com/android/dialer/voicemail/VoicemailActivityInstrumentationTestCase2.java b/tests/src/com/android/dialer/voicemail/VoicemailActivityInstrumentationTestCase2.java index a992e8bd0..cabaf6732 100644 --- a/tests/src/com/android/dialer/voicemail/VoicemailActivityInstrumentationTestCase2.java +++ b/tests/src/com/android/dialer/voicemail/VoicemailActivityInstrumentationTestCase2.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2016 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.dialer.voicemail; import android.app.Activity; @@ -23,7 +39,6 @@ import java.util.Locale; import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_FOR_CONTENT; - /** * Common methods and attributes between {@link VoicemailArchiveTest} and * {@link VoicemailPlaybackTest}. diff --git a/tests/src/com/android/dialer/voicemail/VoicemailArchiveTest.java b/tests/src/com/android/dialer/voicemail/VoicemailArchiveTest.java index 8cbd344a2..bbd5edc48 100644 --- a/tests/src/com/android/dialer/voicemail/VoicemailArchiveTest.java +++ b/tests/src/com/android/dialer/voicemail/VoicemailArchiveTest.java @@ -15,15 +15,13 @@ */ package com.android.dialer.voicemail; + import android.content.ContentUris; import android.content.ContentValues; +import android.content.res.AssetManager; -import com.android.dialer.R; import com.android.dialer.database.VoicemailArchiveContract.VoicemailArchive; -import android.content.res.AssetManager; -import android.test.suitebuilder.annotation.Suppress; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; diff --git a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java index abd582bec..be9905edd 100644 --- a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java +++ b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2016 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. @@ -16,10 +16,14 @@ package com.android.dialer.voicemail; +import android.content.ContentUris; +import android.database.Cursor; +import android.net.Uri; import android.test.suitebuilder.annotation.Suppress; import com.android.dialer.calllog.CallLogActivity; - +import com.android.dialer.database.VoicemailArchiveContract; +import static com.android.dialer.voicemail.VoicemailAsyncTaskUtil.Tasks.ARCHIVE_VOICEMAIL_CONTENT; import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_FOR_CONTENT; /** @@ -38,6 +42,12 @@ public class VoicemailPlaybackTest mPresenter = VoicemailPlaybackPresenter.getInstance(getActivity(), null); } + @Override + public void tearDown() throws Exception { + cleanUpArchivedVoicemailUri(); + super.tearDown(); + } + @Suppress public void testWhenCheckForContentCompletes() throws Throwable { setUriForRealFileVoicemailEntry(); @@ -54,4 +64,82 @@ public class VoicemailPlaybackTest assertStateTextContains("Loading voicemail"); } + + public void testArchiveContent() throws Throwable { + setUriForRealFileVoicemailEntry(); + setPlaybackViewForPresenter(); + mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); + + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + mPresenter.archiveContent(mVoicemailUri, true); + } + }); + mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); + mFakeAsyncTaskExecutor.runTask(ARCHIVE_VOICEMAIL_CONTENT); + getInstrumentation().waitForIdleSync(); + assertVoicemailArchived(); + } + + public void testShareContent() throws Throwable { + setUriForRealFileVoicemailEntry(); + setPlaybackViewForPresenter(); + mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); + + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + mPresenter.archiveContent(mVoicemailUri, false); + } + }); + mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); + mFakeAsyncTaskExecutor.runTask(ARCHIVE_VOICEMAIL_CONTENT); + getInstrumentation().waitForIdleSync(); + assertVoicemailArchived(); + } + + private void assertVoicemailArchived() { + try (Cursor cursor = getArchivedVoicemailCursor()) { + assertTrue(hasContent(cursor)); + assertEquals(ContentUris.parseId(mVoicemailUri), getRowServerId(cursor)); + } catch (Exception e) { + fail("Voicemail was not archived: " + e.toString()); + } + } + + private void cleanUpArchivedVoicemailUri() { + try (Cursor cursor = getArchivedVoicemailCursor()) { + if (hasContent(cursor)) { + getContentResolver().delete(getRowUri(cursor), null, null); + } + } + } + + private Cursor getArchivedVoicemailCursor() { + return getContentResolver().query( + VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, + new String[] { + VoicemailArchiveContract.VoicemailArchive._ID, + VoicemailArchiveContract.VoicemailArchive.SERVER_ID + }, + VoicemailArchiveContract.VoicemailArchive.SERVER_ID + "=" + + ContentUris.parseId(mVoicemailUri), + null, + null); + } + + private int getRowServerId(Cursor cursor) { + return cursor + .getInt(cursor.getColumnIndex(VoicemailArchiveContract.VoicemailArchive.SERVER_ID)); + } + + private Uri getRowUri(Cursor cursor) { + return VoicemailArchiveContract.VoicemailArchive.buildWithId(cursor.getInt( + cursor.getColumnIndex(VoicemailArchiveContract.VoicemailArchive._ID))); + } + + private boolean hasContent(Cursor cursor) { + return cursor != null && cursor.moveToFirst(); + } } \ No newline at end of file -- cgit v1.2.3