Move RingtoneManager out of the activity and into the ViewModel.
And add unit tests to cover the new logic in the ViewModel Bug: 275540178 Test: com.android.soundpicker.RingtonePickerViewModelTest Change-Id: Ib160c82e3661eb4b901c4eea3274037f9f93e25a
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.soundpicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* A factory class used to create {@link Ringtone}.
|
||||
*/
|
||||
public class RingtoneFactory {
|
||||
|
||||
private final Context mApplicationContext;
|
||||
|
||||
RingtoneFactory(Context context) {
|
||||
mApplicationContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Ringtone} based on the provided URI.
|
||||
*
|
||||
* @param uri The URI used to get the {@link Ringtone}
|
||||
* @return a {@link Ringtone}
|
||||
*/
|
||||
public Ringtone create(Uri uri) {
|
||||
return RingtoneManager.getRingtone(mApplicationContext, uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.soundpicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.RingtoneManager;
|
||||
|
||||
/**
|
||||
* A factory class used to create {@link RingtoneManager}.
|
||||
*/
|
||||
public class RingtoneManagerFactory {
|
||||
|
||||
private final Context mApplicationContext;
|
||||
|
||||
RingtoneManagerFactory(Context context) {
|
||||
mApplicationContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link RingtoneManager} and returns it.
|
||||
*
|
||||
* @return a {@link RingtoneManager}
|
||||
*/
|
||||
public RingtoneManager create() {
|
||||
return new RingtoneManager(mApplicationContext, /* includeParentRingtones */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import android.content.res.Resources;
|
||||
import android.content.res.Resources.NotFoundException;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWrapper;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
@@ -50,7 +48,6 @@ import android.widget.Toast;
|
||||
import com.android.internal.app.AlertActivity;
|
||||
import com.android.internal.app.AlertController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -77,31 +74,20 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
|
||||
private static final int ADD_FILE_REQUEST_CODE = 300;
|
||||
|
||||
private RingtoneManager mRingtoneManager;
|
||||
private RingtonePickerViewModel mRingtonePickerViewModel;
|
||||
|
||||
private int mType;
|
||||
|
||||
private Cursor mCursor;
|
||||
private Handler mHandler;
|
||||
private BadgedRingtoneAdapter mAdapter;
|
||||
|
||||
/** The position in the list of the 'Silent' item. */
|
||||
private int mSilentPos = POS_UNKNOWN;
|
||||
|
||||
/** The position in the list of the 'Default' item. */
|
||||
private int mDefaultRingtonePos = POS_UNKNOWN;
|
||||
|
||||
/** The position in the list of the ringtone to sample. */
|
||||
private int mSampleRingtonePos = POS_UNKNOWN;
|
||||
|
||||
/** Whether this list has the 'Silent' item. */
|
||||
private boolean mHasSilentItem;
|
||||
|
||||
/** The Uri to place a checkmark next to. */
|
||||
private Uri mExistingUri;
|
||||
|
||||
/** The number of static items in the list. */
|
||||
private int mStaticItemCount;
|
||||
|
||||
/** Whether this list has the 'Default' item. */
|
||||
private boolean mHasDefaultItem;
|
||||
|
||||
@@ -111,22 +97,6 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
/** Id of the user to which the ringtone picker should list the ringtones */
|
||||
private int mPickerUserId;
|
||||
|
||||
/** Context of the user specified by mPickerUserId */
|
||||
private Context mTargetContext;
|
||||
|
||||
/**
|
||||
* A Ringtone for the default ringtone. In most cases, the RingtoneManager
|
||||
* will stop the previous ringtone. However, the RingtoneManager doesn't
|
||||
* manage the default ringtone for us, so we should stop this one manually.
|
||||
*/
|
||||
private Ringtone mDefaultRingtone;
|
||||
|
||||
/**
|
||||
* The ringtone that's currently playing, unless the currently playing one is the default
|
||||
* ringtone.
|
||||
*/
|
||||
private Ringtone mCurrentRingtone;
|
||||
|
||||
/**
|
||||
* Stable ID for the ringtone that is currently checked (may be -1 if no ringtone is checked).
|
||||
*/
|
||||
@@ -136,20 +106,14 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
|
||||
private boolean mShowOkCancelButtons;
|
||||
|
||||
/**
|
||||
* Keep the currently playing ringtone around when changing orientation, so that it
|
||||
* can be stopped later, after the activity is recreated.
|
||||
*/
|
||||
private static Ringtone sPlayingRingtone;
|
||||
|
||||
private DialogInterface.OnClickListener mRingtoneClickListener =
|
||||
private final DialogInterface.OnClickListener mRingtoneClickListener =
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
/*
|
||||
* On item clicked
|
||||
*/
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which == mCursor.getCount() + mStaticItemCount) {
|
||||
if (which == mCursor.getCount() + mRingtonePickerViewModel.getFixedItemCount()) {
|
||||
// The "Add new ringtone" item was clicked. Start a file picker intent to select
|
||||
// only audio files (MIME type "audio/*")
|
||||
final Intent chooseFile = getMediaFilePickerIntent();
|
||||
@@ -163,7 +127,9 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
// In the buttonless (watch-only) version, preemptively set our result since we won't
|
||||
// have another chance to do so before the activity closes.
|
||||
if (!mShowOkCancelButtons) {
|
||||
setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
|
||||
setSuccessResultWithRingtone(
|
||||
mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri(getCheckedItem(),
|
||||
mUriForDefaultItem));
|
||||
}
|
||||
|
||||
// Play clip
|
||||
@@ -175,16 +141,18 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mRingtonePickerViewModel = new RingtonePickerViewModel(
|
||||
new RingtoneManagerFactory(this), new RingtoneFactory(this));
|
||||
mHandler = new Handler();
|
||||
|
||||
Intent intent = getIntent();
|
||||
mPickerUserId = UserHandle.myUserId();
|
||||
mTargetContext = this;
|
||||
|
||||
// Get the types of ringtones to show
|
||||
mType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1);
|
||||
initRingtoneManager();
|
||||
mType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,
|
||||
RingtonePickerViewModel.RINGTONE_TYPE_UNKNOWN);
|
||||
mRingtonePickerViewModel.setRingtoneType(mType);
|
||||
setupCursor();
|
||||
|
||||
/*
|
||||
* Get whether to show the 'Default' item, and the URI to play when the
|
||||
@@ -206,7 +174,7 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
mShowOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
|
||||
|
||||
// The volume keys will control the stream that we are choosing a ringtone for
|
||||
setVolumeControlStream(mRingtoneManager.inferStreamType());
|
||||
setVolumeControlStream(mRingtonePickerViewModel.getRingtoneStreamType());
|
||||
|
||||
// Get the URI whose list item should have a checkmark
|
||||
mExistingUri = intent
|
||||
@@ -262,12 +230,7 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
final AsyncTask<Uri, Void, Uri> installTask = new AsyncTask<Uri, Void, Uri>() {
|
||||
@Override
|
||||
protected Uri doInBackground(Uri... params) {
|
||||
try {
|
||||
return mRingtoneManager.addCustomExternalRingtone(params[0], mType);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Log.e(TAG, "Unable to add new ringtone", e);
|
||||
}
|
||||
return null;
|
||||
return mRingtonePickerViewModel.addRingtone(params[0], mType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -335,33 +298,36 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
|
||||
public void onPrepareListView(ListView listView) {
|
||||
// Reset the static item count, as this method can be called multiple times
|
||||
mStaticItemCount = 0;
|
||||
mRingtonePickerViewModel.resetFixedItemCount();
|
||||
|
||||
if (mHasDefaultItem) {
|
||||
mDefaultRingtonePos = addDefaultRingtoneItem(listView);
|
||||
int defaultItemPos = addDefaultRingtoneItem(listView);
|
||||
|
||||
if (getCheckedItem() == POS_UNKNOWN && RingtoneManager.isDefault(mExistingUri)) {
|
||||
setCheckedItem(mDefaultRingtonePos);
|
||||
setCheckedItem(defaultItemPos);
|
||||
}
|
||||
}
|
||||
|
||||
if (mHasSilentItem) {
|
||||
mSilentPos = addSilentItem(listView);
|
||||
int silentItemPos = addSilentItem(listView);
|
||||
|
||||
// The 'Silent' item should use a null Uri
|
||||
if (getCheckedItem() == POS_UNKNOWN && mExistingUri == null) {
|
||||
setCheckedItem(mSilentPos);
|
||||
setCheckedItem(silentItemPos);
|
||||
}
|
||||
}
|
||||
|
||||
if (getCheckedItem() == POS_UNKNOWN) {
|
||||
setCheckedItem(getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri)));
|
||||
setCheckedItem(
|
||||
getListPosition(mRingtonePickerViewModel.getRingtonePosition(mExistingUri)));
|
||||
}
|
||||
|
||||
// In the buttonless (watch-only) version, preemptively set our result since we won't
|
||||
// have another chance to do so before the activity closes.
|
||||
if (!mShowOkCancelButtons) {
|
||||
setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
|
||||
setSuccessResultWithRingtone(
|
||||
mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri(getCheckedItem(),
|
||||
mUriForDefaultItem));
|
||||
}
|
||||
// If external storage is available, add a button to install sounds from storage.
|
||||
if (resolvesMediaFilePicker()
|
||||
@@ -381,7 +347,8 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
*/
|
||||
private void requeryForAdapter() {
|
||||
// Refresh and set a new cursor, closing the old one.
|
||||
initRingtoneManager();
|
||||
mRingtonePickerViewModel.initRingtoneManager(mType);
|
||||
setupCursor();
|
||||
mAdapter.changeCursor(mCursor);
|
||||
|
||||
// Update checked item location.
|
||||
@@ -393,7 +360,7 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
}
|
||||
}
|
||||
if (mHasSilentItem && checkedPosition == POS_UNKNOWN) {
|
||||
checkedPosition = mSilentPos;
|
||||
checkedPosition = mRingtonePickerViewModel.getSilentItemPosition();
|
||||
}
|
||||
setCheckedItem(checkedPosition);
|
||||
setupAlert();
|
||||
@@ -412,17 +379,21 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
com.android.internal.R.layout.select_dialog_singlechoice_material, listView, false);
|
||||
textView.setText(textResId);
|
||||
listView.addHeaderView(textView);
|
||||
mStaticItemCount++;
|
||||
mRingtonePickerViewModel.incrementFixedItemCount();
|
||||
return listView.getHeaderViewsCount() - 1;
|
||||
}
|
||||
|
||||
private int addDefaultRingtoneItem(ListView listView) {
|
||||
return addStaticItem(listView,
|
||||
int defaultRingtoneItemPos = addStaticItem(listView,
|
||||
RingtonePickerViewModel.getDefaultRingtoneItemTextByType(mType));
|
||||
mRingtonePickerViewModel.setDefaultItemPosition(defaultRingtoneItemPos);
|
||||
return defaultRingtoneItemPos;
|
||||
}
|
||||
|
||||
private int addSilentItem(ListView listView) {
|
||||
return addStaticItem(listView, com.android.internal.R.string.ringtone_silent);
|
||||
int silentItemPos = addStaticItem(listView, com.android.internal.R.string.ringtone_silent);
|
||||
mRingtonePickerViewModel.setSilentItemPosition(silentItemPos);
|
||||
return silentItemPos;
|
||||
}
|
||||
|
||||
private void addNewSoundItem(ListView listView) {
|
||||
@@ -435,21 +406,9 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
listView.addFooterView(view);
|
||||
}
|
||||
|
||||
private void initRingtoneManager() {
|
||||
// Reinstantiate the RingtoneManager. Cursor.requery() was deprecated and calling it
|
||||
// causes unexpected behavior.
|
||||
mRingtoneManager = new RingtoneManager(mTargetContext, /* includeParentRingtones */ true);
|
||||
if (mType != -1) {
|
||||
mRingtoneManager.setType(mType);
|
||||
}
|
||||
mCursor = new LocalizedCursor(mRingtoneManager.getCursor(), getResources(), COLUMN_LABEL);
|
||||
}
|
||||
|
||||
private Ringtone getRingtone(int ringtoneManagerPosition) {
|
||||
if (ringtoneManagerPosition < 0) {
|
||||
return null;
|
||||
}
|
||||
return mRingtoneManager.getRingtone(ringtoneManagerPosition);
|
||||
private void setupCursor() {
|
||||
mCursor = new LocalizedCursor(
|
||||
mRingtonePickerViewModel.getRingtoneCursor(), getResources(), COLUMN_LABEL);
|
||||
}
|
||||
|
||||
private int getCheckedItem() {
|
||||
@@ -458,7 +417,8 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
|
||||
private void setCheckedItem(int pos) {
|
||||
mAlertParams.mCheckedItem = pos;
|
||||
mCheckedItemId = mAdapter.getItemId(getRingtoneManagerPosition(pos));
|
||||
mCheckedItemId = mAdapter.getItemId(
|
||||
mRingtonePickerViewModel.itemPositionToRingtonePosition(pos));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -467,11 +427,10 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE;
|
||||
|
||||
// Stop playing the previous ringtone
|
||||
mRingtoneManager.stopPreviousRingtone();
|
||||
|
||||
if (positiveResult) {
|
||||
setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
|
||||
setSuccessResultWithRingtone(
|
||||
mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri(getCheckedItem(),
|
||||
mUriForDefaultItem));
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
}
|
||||
@@ -484,7 +443,7 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
*/
|
||||
public void onItemSelected(AdapterView parent, View view, int position, long id) {
|
||||
// footer view
|
||||
if (position >= mCursor.getCount() + mStaticItemCount) {
|
||||
if (position >= mCursor.getCount() + mRingtonePickerViewModel.getFixedItemCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -493,7 +452,9 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
// In the buttonless (watch-only) version, preemptively set our result since we won't
|
||||
// have another chance to do so before the activity closes.
|
||||
if (!mShowOkCancelButtons) {
|
||||
setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
|
||||
setSuccessResultWithRingtone(
|
||||
mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri(getCheckedItem(),
|
||||
mUriForDefaultItem));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,63 +463,27 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
|
||||
private void playRingtone(int position, int delayMs) {
|
||||
mHandler.removeCallbacks(this);
|
||||
mSampleRingtonePos = position;
|
||||
mRingtonePickerViewModel.setSampleItemPosition(position);
|
||||
mHandler.postDelayed(this, delayMs);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
stopAnyPlayingRingtone();
|
||||
if (mSampleRingtonePos == mSilentPos) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ringtone ringtone;
|
||||
if (mSampleRingtonePos == mDefaultRingtonePos) {
|
||||
if (mDefaultRingtone == null) {
|
||||
mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem);
|
||||
}
|
||||
/*
|
||||
* Stream type of mDefaultRingtone is not set explicitly here.
|
||||
* It should be set in accordance with mRingtoneManager of this Activity.
|
||||
*/
|
||||
if (mDefaultRingtone != null) {
|
||||
mDefaultRingtone.setStreamType(mRingtoneManager.inferStreamType());
|
||||
}
|
||||
ringtone = mDefaultRingtone;
|
||||
mCurrentRingtone = null;
|
||||
} else {
|
||||
ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos));
|
||||
mCurrentRingtone = ringtone;
|
||||
}
|
||||
|
||||
if (ringtone != null) {
|
||||
if (mAttributesFlags != 0) {
|
||||
ringtone.setAudioAttributes(
|
||||
new AudioAttributes.Builder(ringtone.getAudioAttributes())
|
||||
.setFlags(mAttributesFlags)
|
||||
.build());
|
||||
}
|
||||
ringtone.play();
|
||||
}
|
||||
mRingtonePickerViewModel.playRingtone(
|
||||
mRingtonePickerViewModel.itemPositionToRingtonePosition(
|
||||
mRingtonePickerViewModel.getSampleItemPosition()), mUriForDefaultItem,
|
||||
mAttributesFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
if (!isChangingConfigurations()) {
|
||||
stopAnyPlayingRingtone();
|
||||
} else {
|
||||
saveAnyPlayingRingtone();
|
||||
}
|
||||
mRingtonePickerViewModel.onStop(isChangingConfigurations());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
if (!isChangingConfigurations()) {
|
||||
stopAnyPlayingRingtone();
|
||||
}
|
||||
mRingtonePickerViewModel.onPause(isChangingConfigurations());
|
||||
}
|
||||
|
||||
private void setSuccessResultWithRingtone(Uri ringtoneUri) {
|
||||
@@ -566,55 +491,13 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, ringtoneUri));
|
||||
}
|
||||
|
||||
private Uri getCurrentlySelectedRingtoneUri() {
|
||||
if (getCheckedItem() == POS_UNKNOWN) {
|
||||
// When the getCheckItem is POS_UNKNOWN, it is not the case we expected.
|
||||
// We return null for this case.
|
||||
return null;
|
||||
} else if (getCheckedItem() == mDefaultRingtonePos) {
|
||||
// Use the default Uri that they originally gave us.
|
||||
return mUriForDefaultItem;
|
||||
} else if (getCheckedItem() == mSilentPos) {
|
||||
// Use a null Uri for the 'Silent' item.
|
||||
return null;
|
||||
} else {
|
||||
return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem()));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAnyPlayingRingtone() {
|
||||
if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
|
||||
sPlayingRingtone = mDefaultRingtone;
|
||||
} else if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) {
|
||||
sPlayingRingtone = mCurrentRingtone;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopAnyPlayingRingtone() {
|
||||
if (sPlayingRingtone != null && sPlayingRingtone.isPlaying()) {
|
||||
sPlayingRingtone.stop();
|
||||
}
|
||||
sPlayingRingtone = null;
|
||||
|
||||
if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
|
||||
mDefaultRingtone.stop();
|
||||
}
|
||||
|
||||
if (mRingtoneManager != null) {
|
||||
mRingtoneManager.stopPreviousRingtone();
|
||||
}
|
||||
}
|
||||
|
||||
private int getRingtoneManagerPosition(int listPos) {
|
||||
return listPos - mStaticItemCount;
|
||||
}
|
||||
|
||||
private int getListPosition(int ringtoneManagerPos) {
|
||||
|
||||
// If the manager position is -1 (for not found), return that
|
||||
if (ringtoneManagerPos < 0) return ringtoneManagerPos;
|
||||
|
||||
return ringtoneManagerPos + mStaticItemCount;
|
||||
return ringtoneManagerPos + mRingtonePickerViewModel.getFixedItemCount();
|
||||
}
|
||||
|
||||
private Intent getMediaFilePickerIntent() {
|
||||
@@ -735,7 +618,7 @@ public final class RingtonePickerActivity extends AlertActivity implements
|
||||
* ringtone Uri is in external storage, and either the uri has no user id or has the
|
||||
* id of the picker user
|
||||
*/
|
||||
Uri currentUri = mRingtoneManager.getRingtoneUri(cursor.getPosition());
|
||||
Uri currentUri = mRingtonePickerViewModel.getRingtoneUri(cursor.getPosition());
|
||||
int uriUserId = ContentProvider.getUserIdFromUri(currentUri, mPickerUserId);
|
||||
Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(currentUri);
|
||||
|
||||
|
||||
@@ -16,16 +16,61 @@
|
||||
|
||||
package com.android.soundpicker;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.StringRes;
|
||||
import android.database.Cursor;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* View model for {@link RingtonePickerActivity}.
|
||||
*/
|
||||
public final class RingtonePickerViewModel {
|
||||
|
||||
static final int RINGTONE_TYPE_UNKNOWN = -1;
|
||||
/**
|
||||
* Keep the currently playing ringtone around when changing orientation, so that it
|
||||
* can be stopped later, after the activity is recreated.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static Ringtone sPlayingRingtone;
|
||||
private static final String TAG = "RingtonePickerViewModel";
|
||||
private static final int ITEM_POSITION_UNKNOWN = -1;
|
||||
|
||||
private final RingtoneManagerFactory mRingtoneManagerFactory;
|
||||
private final RingtoneFactory mRingtoneFactory;
|
||||
|
||||
/** The position in the list of the 'Silent' item. */
|
||||
private int mSilentItemPosition = ITEM_POSITION_UNKNOWN;
|
||||
/** The position in the list of the ringtone to sample. */
|
||||
private int mSampleItemPosition = ITEM_POSITION_UNKNOWN;
|
||||
/** The position in the list of the 'Default' item. */
|
||||
private int mDefaultItemPosition = ITEM_POSITION_UNKNOWN;
|
||||
/** The number of static items in the list. */
|
||||
private int mFixedItemCount;
|
||||
|
||||
private RingtoneManager mRingtoneManager;
|
||||
|
||||
/**
|
||||
* The ringtone that's currently playing.
|
||||
*/
|
||||
private Ringtone mCurrentRingtone;
|
||||
|
||||
RingtonePickerViewModel(RingtoneManagerFactory ringtoneManagerFactory,
|
||||
RingtoneFactory ringtoneFactory) {
|
||||
mRingtoneManagerFactory = ringtoneManagerFactory;
|
||||
mRingtoneFactory = ringtoneFactory;
|
||||
initRingtoneManager(RINGTONE_TYPE_UNKNOWN);
|
||||
}
|
||||
|
||||
@StringRes
|
||||
static int getTitleByType(int ringtoneType) {
|
||||
switch (ringtoneType) {
|
||||
@@ -72,4 +117,168 @@ public final class RingtonePickerViewModel {
|
||||
return R.string.ringtone_default;
|
||||
}
|
||||
}
|
||||
|
||||
void initRingtoneManager(int type) {
|
||||
mRingtoneManager = mRingtoneManagerFactory.create();
|
||||
setRingtoneType(type);
|
||||
}
|
||||
|
||||
void setRingtoneType(int type) {
|
||||
if (type != RINGTONE_TYPE_UNKNOWN) {
|
||||
mRingtoneManager.setType(type);
|
||||
}
|
||||
}
|
||||
|
||||
int getRingtoneStreamType() {
|
||||
return mRingtoneManager.inferStreamType();
|
||||
}
|
||||
|
||||
Cursor getRingtoneCursor() {
|
||||
return mRingtoneManager.getCursor();
|
||||
}
|
||||
|
||||
Uri getRingtoneUri(int ringtonePosition) {
|
||||
return mRingtoneManager.getRingtoneUri(ringtonePosition);
|
||||
}
|
||||
|
||||
int getRingtonePosition(Uri uri) {
|
||||
return mRingtoneManager.getRingtonePosition(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the item in the list before header views were added.
|
||||
*
|
||||
* @param itemPosition the position of item in the list with any added headers.
|
||||
* @return position of the item in the list ignoring headers.
|
||||
*/
|
||||
int itemPositionToRingtonePosition(int itemPosition) {
|
||||
return itemPosition - mFixedItemCount;
|
||||
}
|
||||
|
||||
int getFixedItemCount() {
|
||||
return mFixedItemCount;
|
||||
}
|
||||
|
||||
void resetFixedItemCount() {
|
||||
mFixedItemCount = 0;
|
||||
}
|
||||
|
||||
void incrementFixedItemCount() {
|
||||
mFixedItemCount++;
|
||||
}
|
||||
|
||||
void setDefaultItemPosition(int defaultItemPosition) {
|
||||
mDefaultItemPosition = defaultItemPosition;
|
||||
}
|
||||
|
||||
int getSilentItemPosition() {
|
||||
return mSilentItemPosition;
|
||||
}
|
||||
|
||||
void setSilentItemPosition(int silentItemPosition) {
|
||||
mSilentItemPosition = silentItemPosition;
|
||||
}
|
||||
|
||||
public int getSampleItemPosition() {
|
||||
return mSampleItemPosition;
|
||||
}
|
||||
|
||||
public void setSampleItemPosition(int sampleItemPosition) {
|
||||
mSampleItemPosition = sampleItemPosition;
|
||||
}
|
||||
|
||||
void onPause(boolean isChangingConfigurations) {
|
||||
if (!isChangingConfigurations) {
|
||||
stopAnyPlayingRingtone();
|
||||
}
|
||||
}
|
||||
|
||||
void onStop(boolean isChangingConfigurations) {
|
||||
if (isChangingConfigurations) {
|
||||
saveAnyPlayingRingtone();
|
||||
} else {
|
||||
stopAnyPlayingRingtone();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Uri getCurrentlySelectedRingtoneUri(int checkedItem, Uri defaultUri) {
|
||||
if (checkedItem == ITEM_POSITION_UNKNOWN) {
|
||||
// When the getCheckItem is POS_UNKNOWN, it is not the case we expected.
|
||||
// We return null for this case.
|
||||
return null;
|
||||
} else if (checkedItem == mDefaultItemPosition) {
|
||||
// Use the default Uri that they originally gave us.
|
||||
return defaultUri;
|
||||
} else if (checkedItem == mSilentItemPosition) {
|
||||
// Use a null Uri for the 'Silent' item.
|
||||
return null;
|
||||
} else {
|
||||
return getRingtoneUri(itemPositionToRingtonePosition(checkedItem));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an audio file to the list of ringtones.
|
||||
* @param uri Uri of the file to be added as ringtone. Must be a media file.
|
||||
* @param type The type of the ringtone to be added.
|
||||
* @return The Uri of the installed ringtone, which may be the {@code uri} if it
|
||||
* is already in ringtone storage. Or null if it failed to add the audio file.
|
||||
*/
|
||||
@Nullable
|
||||
Uri addRingtone(Uri uri, int type) {
|
||||
try {
|
||||
return mRingtoneManager.addCustomExternalRingtone(uri, type);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Log.e(TAG, "Unable to add new ringtone", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void playRingtone(int position, Uri uriForDefaultItem, int attributesFlags) {
|
||||
stopAnyPlayingRingtone();
|
||||
if (mSampleItemPosition == mSilentItemPosition) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSampleItemPosition == mDefaultItemPosition) {
|
||||
mCurrentRingtone = mRingtoneFactory.create(uriForDefaultItem);
|
||||
/*
|
||||
* Stream type of mDefaultRingtone is not set explicitly here. It should be set in
|
||||
* accordance with mRingtoneManager of this Activity.
|
||||
*/
|
||||
if (mCurrentRingtone != null) {
|
||||
mCurrentRingtone.setStreamType(mRingtoneManager.inferStreamType());
|
||||
}
|
||||
} else {
|
||||
mCurrentRingtone = mRingtoneManager.getRingtone(position);
|
||||
}
|
||||
|
||||
if (mCurrentRingtone != null) {
|
||||
if (attributesFlags != 0) {
|
||||
mCurrentRingtone.setAudioAttributes(new AudioAttributes.Builder(
|
||||
mCurrentRingtone.getAudioAttributes()).setFlags(attributesFlags).build());
|
||||
}
|
||||
mCurrentRingtone.play();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAnyPlayingRingtone() {
|
||||
if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) {
|
||||
sPlayingRingtone = mCurrentRingtone;
|
||||
}
|
||||
mCurrentRingtone = null;
|
||||
}
|
||||
|
||||
private void stopAnyPlayingRingtone() {
|
||||
if (sPlayingRingtone != null && sPlayingRingtone.isPlaying()) {
|
||||
sPlayingRingtone.stop();
|
||||
}
|
||||
sPlayingRingtone = null;
|
||||
|
||||
if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) {
|
||||
mCurrentRingtone.stop();
|
||||
}
|
||||
mCurrentRingtone = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,19 +17,294 @@
|
||||
package com.android.soundpicker;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RingtonePickerViewModelTest {
|
||||
|
||||
private static final Uri DEFAULT_URI = Uri.parse("https://www.google.com/login.html");
|
||||
private static final int POS_UNKNOWN = -1;
|
||||
private static final int NO_ATTRIBUTES_FLAGS = 0;
|
||||
private static final int SILENT_RINGTONE_POSITION = 0;
|
||||
private static final int DEFAULT_RINGTONE_POSITION = 1;
|
||||
private static final int RINGTONE_POSITION = 2;
|
||||
|
||||
@Mock
|
||||
private RingtoneManagerFactory mMockRingtoneManagerFactory;
|
||||
@Mock
|
||||
private RingtoneFactory mMockRingtoneFactory;
|
||||
@Mock
|
||||
private RingtoneManager mMockRingtoneManager;
|
||||
@Mock
|
||||
private Cursor mMockCursor;
|
||||
|
||||
private Ringtone mMockDefaultRingtone;
|
||||
private Ringtone mMockRingtone;
|
||||
private RingtonePickerViewModel mViewModel;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mMockRingtoneManagerFactory.create()).thenReturn(mMockRingtoneManager);
|
||||
mMockDefaultRingtone = createMockRingtone();
|
||||
mMockRingtone = createMockRingtone();
|
||||
when(mMockRingtoneFactory.create(DEFAULT_URI)).thenReturn(mMockDefaultRingtone);
|
||||
when(mMockRingtoneManager.getRingtone(anyInt())).thenReturn(mMockRingtone);
|
||||
|
||||
mViewModel = new RingtonePickerViewModel(mMockRingtoneManagerFactory, mMockRingtoneFactory);
|
||||
|
||||
mViewModel.setSilentItemPosition(SILENT_RINGTONE_POSITION);
|
||||
mViewModel.setDefaultItemPosition(DEFAULT_RINGTONE_POSITION);
|
||||
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStreamType_returnsTheCorrectStreamType() {
|
||||
when(mMockRingtoneManager.inferStreamType()).thenReturn(AudioManager.STREAM_ALARM);
|
||||
assertEquals(mViewModel.getRingtoneStreamType(), AudioManager.STREAM_ALARM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRingtoneCursor_returnsTheCorrectRingtoneCursor() {
|
||||
when(mMockRingtoneManager.getCursor()).thenReturn(mMockCursor);
|
||||
assertEquals(mViewModel.getRingtoneCursor(), mMockCursor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRingtoneUri_returnsTheCorrectRingtoneUri() {
|
||||
Uri expectedUri = DEFAULT_URI;
|
||||
when(mMockRingtoneManager.getRingtoneUri(anyInt())).thenReturn(expectedUri);
|
||||
Uri actualUri = mViewModel.getRingtoneUri(DEFAULT_RINGTONE_POSITION);
|
||||
assertEquals(actualUri, expectedUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPause_withChangingConfigurationTrue_doNotStopPlayingRingtone() {
|
||||
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockRingtone);
|
||||
mViewModel.onPause(/* isChangingConfigurations= */ true);
|
||||
verify(mMockRingtone, never()).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPause_withChangingConfigurationFalse_stopPlayingRingtone() {
|
||||
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
|
||||
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone);
|
||||
mViewModel.onPause(/* isChangingConfigurations= */ false);
|
||||
verify(mMockDefaultRingtone).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnViewModelRecreated_previousRingtoneCanStillBeStopped() {
|
||||
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
|
||||
Ringtone mockRingtone1 = createMockRingtone();
|
||||
Ringtone mockRingtone2 = createMockRingtone();
|
||||
when(mMockRingtoneManager.getRingtone(anyInt())).thenReturn(mockRingtone1, mockRingtone2);
|
||||
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mockRingtone1);
|
||||
// Fake a scenario where the activity is destroyed and recreated due to a config change.
|
||||
// This will result in a new view model getting created.
|
||||
mViewModel.onStop(/* isChangingConfigurations= */ true);
|
||||
verify(mockRingtone1, never()).stop();
|
||||
mViewModel = new RingtonePickerViewModel(mMockRingtoneManagerFactory, mMockRingtoneFactory);
|
||||
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
|
||||
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mockRingtone2);
|
||||
verify(mockRingtone1).stop();
|
||||
verify(mockRingtone2, never()).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStop_withChangingConfigurationTrueAndDefaultRingtonePlaying_saveRingtone() {
|
||||
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
|
||||
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone);
|
||||
mViewModel.onStop(/* isChangingConfigurations= */ true);
|
||||
assertEquals(RingtonePickerViewModel.sPlayingRingtone, mMockDefaultRingtone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStop_withChangingConfigurationTrueAndCurrentRingtonePlaying_saveRingtone() {
|
||||
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
|
||||
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockRingtone);
|
||||
mViewModel.onStop(/* isChangingConfigurations= */ true);
|
||||
assertEquals(RingtonePickerViewModel.sPlayingRingtone, mMockRingtone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStop_withChangingConfigurationTrueAndNoPlayingRingtone_saveNothing() {
|
||||
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
|
||||
mViewModel.onStop(/* isChangingConfigurations= */ true);
|
||||
assertNull(RingtonePickerViewModel.sPlayingRingtone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStop_withChangingConfigurationFalse_stopPlayingRingtone() {
|
||||
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
|
||||
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone);
|
||||
mViewModel.onStop(/* isChangingConfigurations= */ false);
|
||||
verify(mMockDefaultRingtone).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentlySelectedRingtoneUri_checkedItemIsUnknown_returnsNull() {
|
||||
Uri uri = mViewModel.getCurrentlySelectedRingtoneUri(POS_UNKNOWN, DEFAULT_URI);
|
||||
assertNull(uri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentlySelectedRingtoneUri_checkedItemIsDefaultPos_returnsDefaultUri() {
|
||||
Uri expectedUri = DEFAULT_URI;
|
||||
Uri actualUri = mViewModel.getCurrentlySelectedRingtoneUri(DEFAULT_RINGTONE_POSITION,
|
||||
expectedUri);
|
||||
assertEquals(actualUri, expectedUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentlySelectedRingtoneUri_checkedItemIsSilentPos_returnsNull() {
|
||||
Uri uri = mViewModel.getCurrentlySelectedRingtoneUri(SILENT_RINGTONE_POSITION, DEFAULT_URI);
|
||||
assertNull(uri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddRingtone_returnsTheCorrectUri() throws IOException {
|
||||
Uri expectedUri = DEFAULT_URI;
|
||||
when(mMockRingtoneManager.addCustomExternalRingtone(any(), anyInt())).thenReturn(
|
||||
expectedUri);
|
||||
Uri actualUri = mViewModel.addRingtone(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION);
|
||||
verify(mMockRingtoneManager).addCustomExternalRingtone(DEFAULT_URI,
|
||||
RingtoneManager.TYPE_NOTIFICATION);
|
||||
assertEquals(actualUri, expectedUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCurrentlySelectedRingtoneUri_checkedItemRingtonePos_returnsTheCorrectUri() {
|
||||
Uri expectedUri = DEFAULT_URI;
|
||||
when(mMockRingtoneManager.getRingtoneUri(RINGTONE_POSITION)).thenReturn(expectedUri);
|
||||
Uri actualUri = mViewModel.getCurrentlySelectedRingtoneUri(RINGTONE_POSITION, DEFAULT_URI);
|
||||
|
||||
verify(mMockRingtoneManager).getRingtoneUri(RINGTONE_POSITION);
|
||||
assertEquals(actualUri, expectedUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayRingtone_stopsPreviouslyRunningRingtone() {
|
||||
// Start playing the first ringtone
|
||||
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
|
||||
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone);
|
||||
// Start playing the second ringtone
|
||||
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
|
||||
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockRingtone);
|
||||
|
||||
verify(mMockDefaultRingtone).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayRingtone_samplePosEqualToSilentPos_onlyStopPlayingRingtone() {
|
||||
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
|
||||
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone);
|
||||
|
||||
mViewModel.setSampleItemPosition(SILENT_RINGTONE_POSITION);
|
||||
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verify(mMockDefaultRingtone).stop();
|
||||
// This will be invoked on the first ringtone we play, but not on the second one.
|
||||
verify(mMockRingtoneFactory).create(any());
|
||||
verify(mMockRingtoneManager, never()).getRingtone(anyInt());
|
||||
verify(mMockRingtone, never()).play();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayRingtone_samplePosEqualToDefaultPos_playDefaultRingtone() {
|
||||
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
|
||||
|
||||
when(mMockRingtoneManager.inferStreamType()).thenReturn(AudioManager.STREAM_ALARM);
|
||||
|
||||
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone);
|
||||
|
||||
verify(mMockDefaultRingtone).setStreamType(AudioManager.STREAM_ALARM);
|
||||
verify(mMockDefaultRingtone).play();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayRingtone_samplePosNotEqualToDefaultPos_playRingtone() {
|
||||
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
|
||||
|
||||
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockRingtone);
|
||||
verify(mMockRingtone).setAudioAttributes(
|
||||
audioAttributes(AudioAttributes.USAGE_NOTIFICATION_RINGTONE,
|
||||
AudioAttributes.FLAG_AUDIBILITY_ENFORCED));
|
||||
verify(mMockRingtone).play();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlayRingtone_withNoAttributeFlags_doNotUpdateRingtoneAttributesFlags() {
|
||||
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
|
||||
|
||||
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
|
||||
NO_ATTRIBUTES_FLAGS);
|
||||
verifyRingtonePlayCalledAndMockPlayingState(mMockRingtone);
|
||||
verify(mMockRingtone, never()).setAudioAttributes(any());
|
||||
verify(mMockRingtone).play();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRingtonePosition_returnsTheCorrectRingtonePosition() {
|
||||
int expectedPosition = 1;
|
||||
when(mMockRingtoneManager.getRingtonePosition(any())).thenReturn(expectedPosition);
|
||||
|
||||
int actualPosition = mViewModel.getRingtonePosition(DEFAULT_URI);
|
||||
|
||||
assertEquals(actualPosition, expectedPosition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultItemUri_withNotificationIntent_returnDefaultNotificationUri() {
|
||||
Uri uri = RingtonePickerViewModel.getDefaultItemUriByType(
|
||||
@@ -69,7 +344,7 @@ public class RingtonePickerViewModelTest {
|
||||
|
||||
@Test
|
||||
public void testTitle_withInvalidRingtoneType_returnDefaultRingtoneTitle() {
|
||||
int title = RingtonePickerViewModel.getTitleByType(-1);
|
||||
int title = RingtonePickerViewModel.getTitleByType(/*ringtoneType= */ -1);
|
||||
assertEquals(com.android.internal.R.string.ringtone_picker_title, title);
|
||||
}
|
||||
|
||||
@@ -126,4 +401,25 @@ public class RingtonePickerViewModelTest {
|
||||
int defaultRingtoneItemText = RingtonePickerViewModel.getDefaultRingtoneItemTextByType(-1);
|
||||
assertEquals(R.string.ringtone_default, defaultRingtoneItemText);
|
||||
}
|
||||
|
||||
private Ringtone createMockRingtone() {
|
||||
Ringtone mockRingtone = mock(Ringtone.class);
|
||||
when(mockRingtone.getAudioAttributes()).thenReturn(
|
||||
audioAttributes(AudioAttributes.USAGE_NOTIFICATION_RINGTONE, 0));
|
||||
|
||||
return mockRingtone;
|
||||
}
|
||||
|
||||
private void verifyRingtonePlayCalledAndMockPlayingState(Ringtone ringtone) {
|
||||
verify(ringtone).play();
|
||||
when(ringtone.isPlaying()).thenReturn(true);
|
||||
}
|
||||
|
||||
private static AudioAttributes audioAttributes(int audioUsage, int flags) {
|
||||
return new AudioAttributes.Builder()
|
||||
.setUsage(audioUsage)
|
||||
.setFlags(flags)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user