Merge "Add stop casting button for output switch"
This commit is contained in:
committed by
Android (Google) Code Review
commit
5f73b0cbcd
@@ -11749,4 +11749,6 @@
|
|||||||
<!-- Subtext for showing the option of RTT setting. [CHAR LIMIT=NONE] -->
|
<!-- Subtext for showing the option of RTT setting. [CHAR LIMIT=NONE] -->
|
||||||
<string name="rtt_settings_always_visible"></string>
|
<string name="rtt_settings_always_visible"></string>
|
||||||
|
|
||||||
|
<!-- Button label to stop casting on media device. [CHAR LIMIT=40 -->
|
||||||
|
<string name="media_output_panel_stop_casting_button">Stop casting</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package com.android.settings.panel;
|
package com.android.settings.panel;
|
||||||
|
|
||||||
|
import static androidx.lifecycle.Lifecycle.Event.ON_START;
|
||||||
|
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
|
||||||
|
|
||||||
import static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME;
|
import static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME;
|
||||||
import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI;
|
import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI;
|
||||||
|
|
||||||
@@ -35,9 +38,15 @@ import android.text.TextUtils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.core.graphics.drawable.IconCompat;
|
import androidx.core.graphics.drawable.IconCompat;
|
||||||
|
import androidx.lifecycle.LifecycleObserver;
|
||||||
|
import androidx.lifecycle.OnLifecycleEvent;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
|
import com.android.settingslib.media.InfoMediaDevice;
|
||||||
|
import com.android.settingslib.media.LocalMediaManager;
|
||||||
|
import com.android.settingslib.media.MediaDevice;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -49,13 +58,20 @@ import java.util.List;
|
|||||||
* Displays Media output item
|
* Displays Media output item
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class MediaOutputPanel implements PanelContent {
|
public class MediaOutputPanel implements PanelContent, LocalMediaManager.DeviceCallback,
|
||||||
|
LifecycleObserver {
|
||||||
|
|
||||||
private static final String TAG = "MediaOutputPanel";
|
private static final String TAG = "MediaOutputPanel";
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final String mPackageName;
|
private final String mPackageName;
|
||||||
|
|
||||||
|
private PanelCustomizedButtonCallback mCallback;
|
||||||
|
private boolean mIsCustomizedButtonUsed = true;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
LocalMediaManager mLocalMediaManager;
|
||||||
|
|
||||||
private MediaSessionManager mMediaSessionManager;
|
private MediaSessionManager mMediaSessionManager;
|
||||||
private MediaController mMediaController;
|
private MediaController mMediaController;
|
||||||
|
|
||||||
@@ -65,8 +81,9 @@ public class MediaOutputPanel implements PanelContent {
|
|||||||
|
|
||||||
private MediaOutputPanel(Context context, String packageName) {
|
private MediaOutputPanel(Context context, String packageName) {
|
||||||
mContext = context.getApplicationContext();
|
mContext = context.getApplicationContext();
|
||||||
mPackageName = packageName;
|
mPackageName = TextUtils.isEmpty(packageName) ? "" : packageName;
|
||||||
if (mPackageName != null) {
|
|
||||||
|
if (!TextUtils.isEmpty(mPackageName)) {
|
||||||
mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
|
mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
|
||||||
for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
|
for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
|
||||||
if (TextUtils.equals(controller.getPackageName(), mPackageName)) {
|
if (TextUtils.equals(controller.getPackageName(), mPackageName)) {
|
||||||
@@ -75,6 +92,7 @@ public class MediaOutputPanel implements PanelContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mMediaController == null) {
|
if (mMediaController == null) {
|
||||||
Log.e(TAG, "Unable to find " + mPackageName + " media controller");
|
Log.e(TAG, "Unable to find " + mPackageName + " media controller");
|
||||||
}
|
}
|
||||||
@@ -156,8 +174,69 @@ public class MediaOutputPanel implements PanelContent {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCustomizedButtonUsed() {
|
||||||
|
return mIsCustomizedButtonUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getCustomButtonTitle() {
|
||||||
|
return mContext.getText(R.string.media_output_panel_stop_casting_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClickCustomizedButton() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerCallback(PanelCustomizedButtonCallback callback) {
|
||||||
|
mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMetricsCategory() {
|
public int getMetricsCategory() {
|
||||||
return SettingsEnums.PANEL_MEDIA_OUTPUT;
|
return SettingsEnums.PANEL_MEDIA_OUTPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectedDeviceStateChanged(MediaDevice device, int state) {
|
||||||
|
dispatchCustomButtonStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceListUpdate(List<MediaDevice> devices) {
|
||||||
|
dispatchCustomButtonStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceAttributesChanged() {
|
||||||
|
dispatchCustomButtonStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchCustomButtonStateChanged() {
|
||||||
|
hideCustomButtonIfNecessary();
|
||||||
|
if (mCallback != null) {
|
||||||
|
mCallback.onCustomizedButtonStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideCustomButtonIfNecessary() {
|
||||||
|
final MediaDevice device = mLocalMediaManager.getCurrentConnectedDevice();
|
||||||
|
mIsCustomizedButtonUsed = device instanceof InfoMediaDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(ON_START)
|
||||||
|
public void onStart() {
|
||||||
|
if (mLocalMediaManager == null) {
|
||||||
|
mLocalMediaManager = new LocalMediaManager(mContext, mPackageName, null);
|
||||||
|
}
|
||||||
|
mLocalMediaManager.registerCallback(this);
|
||||||
|
mLocalMediaManager.startScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(ON_STOP)
|
||||||
|
public void onStop() {
|
||||||
|
mLocalMediaManager.unregisterCallback(this);
|
||||||
|
mLocalMediaManager.stopScan();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -74,4 +74,31 @@ public interface PanelContent extends Instrumentable {
|
|||||||
default Intent getHeaderIconIntent() {
|
default Intent getHeaderIconIntent() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} to enable custom button to replace see more button,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
default boolean isCustomizedButtonUsed() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a string for the title of the custom button.
|
||||||
|
*/
|
||||||
|
default CharSequence getCustomButtonTitle() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement the click event for custom button.
|
||||||
|
*/
|
||||||
|
default void onClickCustomizedButton() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register to start receiving callbacks for custom button events.
|
||||||
|
*
|
||||||
|
* @param callback the callback to add.
|
||||||
|
*/
|
||||||
|
default void registerCallback(PanelCustomizedButtonCallback callback) {}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.settings.panel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PanelCustomizedButtonCallback provides a callback interface for {@link PanelFragment} to receive
|
||||||
|
* events from {@link PanelContent}.
|
||||||
|
*/
|
||||||
|
public interface PanelCustomizedButtonCallback {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It will be called when customized button state is changed. For example, custom button
|
||||||
|
* would be hidden for specific behavior.
|
||||||
|
*/
|
||||||
|
void onCustomizedButtonStateChanged();
|
||||||
|
}
|
@@ -41,6 +41,7 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.core.graphics.drawable.IconCompat;
|
import androidx.core.graphics.drawable.IconCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.lifecycle.LifecycleObserver;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
@@ -53,6 +54,7 @@ import com.android.settings.R;
|
|||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settings.panel.PanelLoggingContract.PanelClosedKeys;
|
import com.android.settings.panel.PanelLoggingContract.PanelClosedKeys;
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
import com.google.android.setupdesign.DividerItemDecoration;
|
import com.google.android.setupdesign.DividerItemDecoration;
|
||||||
|
|
||||||
@@ -183,6 +185,11 @@ public class PanelFragment extends Fragment {
|
|||||||
activity.finish();
|
activity.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mPanel.registerCallback(new LocalPanelCallback());
|
||||||
|
if (mPanel instanceof LifecycleObserver) {
|
||||||
|
getLifecycle().addObserver((LifecycleObserver) mPanel);
|
||||||
|
}
|
||||||
|
|
||||||
mMetricsProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider();
|
mMetricsProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider();
|
||||||
|
|
||||||
mPanelSlices.setLayoutManager(new LinearLayoutManager((activity)));
|
mPanelSlices.setLayoutManager(new LinearLayoutManager((activity)));
|
||||||
@@ -208,8 +215,15 @@ public class PanelFragment extends Fragment {
|
|||||||
mSeeMoreButton.setOnClickListener(getSeeMoreListener());
|
mSeeMoreButton.setOnClickListener(getSeeMoreListener());
|
||||||
mDoneButton.setOnClickListener(getCloseListener());
|
mDoneButton.setOnClickListener(getCloseListener());
|
||||||
|
|
||||||
// If getSeeMoreIntent() is null, hide the mSeeMoreButton.
|
if (mPanel.isCustomizedButtonUsed()) {
|
||||||
if (mPanel.getSeeMoreIntent() == null) {
|
final CharSequence customTitle = mPanel.getCustomButtonTitle();
|
||||||
|
if (TextUtils.isEmpty(customTitle)) {
|
||||||
|
mSeeMoreButton.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
mSeeMoreButton.setText(customTitle);
|
||||||
|
}
|
||||||
|
} else if (mPanel.getSeeMoreIntent() == null) {
|
||||||
|
// If getSeeMoreIntent() is null hide the mSeeMoreButton.
|
||||||
mSeeMoreButton.setVisibility(View.GONE);
|
mSeeMoreButton.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,9 +385,13 @@ public class PanelFragment extends Fragment {
|
|||||||
View.OnClickListener getSeeMoreListener() {
|
View.OnClickListener getSeeMoreListener() {
|
||||||
return (v) -> {
|
return (v) -> {
|
||||||
mPanelClosedKey = PanelClosedKeys.KEY_SEE_MORE;
|
mPanelClosedKey = PanelClosedKeys.KEY_SEE_MORE;
|
||||||
final FragmentActivity activity = getActivity();
|
if (mPanel.isCustomizedButtonUsed()) {
|
||||||
activity.startActivityForResult(mPanel.getSeeMoreIntent(), 0);
|
mPanel.onClickCustomizedButton();
|
||||||
activity.finish();
|
} else {
|
||||||
|
final FragmentActivity activity = getActivity();
|
||||||
|
activity.startActivityForResult(mPanel.getSeeMoreIntent(), 0);
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,4 +410,15 @@ public class PanelFragment extends Fragment {
|
|||||||
activity.startActivity(mPanel.getHeaderIconIntent());
|
activity.startActivity(mPanel.getHeaderIconIntent());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LocalPanelCallback implements PanelCustomizedButtonCallback {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCustomizedButtonStateChanged() {
|
||||||
|
ThreadUtils.postOnMainThread(() -> {
|
||||||
|
mSeeMoreButton.setVisibility(
|
||||||
|
mPanel.isCustomizedButtonUsed() ? View.VISIBLE : View.GONE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,9 @@ import static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -32,6 +34,9 @@ import android.net.Uri;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.slices.CustomSliceRegistry;
|
import com.android.settings.slices.CustomSliceRegistry;
|
||||||
|
import com.android.settingslib.media.InfoMediaDevice;
|
||||||
|
import com.android.settingslib.media.LocalMediaManager;
|
||||||
|
import com.android.settingslib.media.PhoneMediaDevice;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -58,6 +63,11 @@ public class MediaOutputPanelTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private MediaMetadata mMediaMetadata;
|
private MediaMetadata mMediaMetadata;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LocalMediaManager mLocalMediaManager;
|
||||||
|
@Mock
|
||||||
|
private PanelCustomizedButtonCallback mCallback;
|
||||||
|
|
||||||
private MediaOutputPanel mPanel;
|
private MediaOutputPanel mPanel;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private List<MediaController> mMediaControllers = new ArrayList<>();
|
private List<MediaController> mMediaControllers = new ArrayList<>();
|
||||||
@@ -67,12 +77,16 @@ public class MediaOutputPanelTest {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
|
|
||||||
mMediaControllers.add(mMediaController);
|
mMediaControllers.add(mMediaController);
|
||||||
when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGENAME);
|
when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGENAME);
|
||||||
when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
|
when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
|
||||||
when(mContext.getApplicationContext()).thenReturn(mContext);
|
when(mContext.getApplicationContext()).thenReturn(mContext);
|
||||||
when(mContext.getSystemService(MediaSessionManager.class)).thenReturn(mMediaSessionManager);
|
when(mContext.getSystemService(MediaSessionManager.class)).thenReturn(mMediaSessionManager);
|
||||||
|
|
||||||
mPanel = MediaOutputPanel.create(mContext, TEST_PACKAGENAME);
|
mPanel = MediaOutputPanel.create(mContext, TEST_PACKAGENAME);
|
||||||
|
mPanel.mLocalMediaManager = mLocalMediaManager;
|
||||||
|
mPanel.registerCallback(mCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -94,6 +108,63 @@ public class MediaOutputPanelTest {
|
|||||||
assertThat(mPanel.getSeeMoreIntent()).isNull();
|
assertThat(mPanel.getSeeMoreIntent()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_shouldRegisterCallback() {
|
||||||
|
mPanel.onStart();
|
||||||
|
|
||||||
|
verify(mLocalMediaManager).registerCallback(any());
|
||||||
|
verify(mLocalMediaManager).startScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStop_shouldUnregisterCallback() {
|
||||||
|
mPanel.onStop();
|
||||||
|
|
||||||
|
verify(mLocalMediaManager).unregisterCallback(any());
|
||||||
|
verify(mLocalMediaManager).stopScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onSelectedDeviceStateChanged_shouldDispatchCustomButtonStateChanged() {
|
||||||
|
mPanel.onSelectedDeviceStateChanged(null, 0);
|
||||||
|
|
||||||
|
verify(mCallback).onCustomizedButtonStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDeviceListUpdate_shouldDispatchCustomButtonStateChanged() {
|
||||||
|
mPanel.onDeviceListUpdate(null);
|
||||||
|
|
||||||
|
verify(mCallback).onCustomizedButtonStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDeviceAttributesChanged_shouldDispatchCustomButtonStateChanged() {
|
||||||
|
mPanel.onDeviceAttributesChanged();
|
||||||
|
|
||||||
|
verify(mCallback).onCustomizedButtonStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void currentConnectDeviceIsInfoDevice_useCustomButtonIsTrue() {
|
||||||
|
final InfoMediaDevice infoMediaDevice = mock(InfoMediaDevice.class);
|
||||||
|
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(infoMediaDevice);
|
||||||
|
|
||||||
|
mPanel.onDeviceAttributesChanged();
|
||||||
|
|
||||||
|
assertThat(mPanel.isCustomizedButtonUsed()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void currentConnectDeviceIsNotInfoDevice_useCustomButtonIsFalse() {
|
||||||
|
final PhoneMediaDevice phoneMediaDevice = mock(PhoneMediaDevice.class);
|
||||||
|
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(phoneMediaDevice);
|
||||||
|
|
||||||
|
mPanel.onDeviceAttributesChanged();
|
||||||
|
|
||||||
|
assertThat(mPanel.isCustomizedButtonUsed()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getTitle_withMetadata_returnArtistName() {
|
public void getTitle_withMetadata_returnArtistName() {
|
||||||
when(mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST)).thenReturn(TEST_ARTIST);
|
when(mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST)).thenReturn(TEST_ARTIST);
|
||||||
|
Reference in New Issue
Block a user