Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a03a9ff authored by Lucas Dupin's avatar Lucas Dupin
Browse files

Song name in KeyguardSliceProvider

This CL also includes synchronization bugfixes, since
SliceProvider#onBindSlice can be called from any thread.

Test: atest KeyguardSliceProviderTest
Test: manual
Bug: 111405682
Change-Id: Ief19625cba96b0c17f7444b2b2acc8f27c24597e
parent 8c9b1803
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2333,4 +2333,7 @@

    <!-- Name for device services grouping system uid apps in Ongoing Privacy Dialog [CHAR_LIMIT=NONE] -->
    <string name="device_services">Device Services</string>

    <!-- What to show on the ambient display player when song doesn't have a title. [CHAR LIMIT=20] -->
    <string name="music_controls_no_title">No title</string>
</resources>
+134 −50
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.keyguard;

import android.annotation.AnyThread;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -24,14 +25,20 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Typeface;
import android.graphics.drawable.Icon;
import android.icu.text.DateFormat;
import android.icu.text.DisplayContext;
import android.media.MediaMetadata;
import android.net.Uri;
import android.os.Handler;
import android.os.Trace;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.StyleSpan;

import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -43,7 +50,9 @@ import androidx.slice.builders.SliceAction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -58,13 +67,17 @@ import java.util.concurrent.TimeUnit;
 * Simple Slice provider that shows the current date.
 */
public class KeyguardSliceProvider extends SliceProvider implements
        NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback {
        NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback,
        NotificationMediaManager.MediaListener {

    private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
    public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
    public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date";
    public static final String KEYGUARD_NEXT_ALARM_URI =
            "content://com.android.systemui.keyguard/alarm";
    public static final String KEYGUARD_DND_URI = "content://com.android.systemui.keyguard/dnd";
    public static final String KEYGUARD_MEDIA_URI =
            "content://com.android.systemui.keyguard/media";
    public static final String KEYGUARD_ACTION_URI =
            "content://com.android.systemui.keyguard/action";

@@ -80,6 +93,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
    protected final Uri mDateUri;
    protected final Uri mAlarmUri;
    protected final Uri mDndUri;
    protected final Uri mMediaUri;
    private final Date mCurrentTime = new Date();
    private final Handler mHandler;
    private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
@@ -94,6 +108,8 @@ public class KeyguardSliceProvider extends SliceProvider implements
    protected ContentResolver mContentResolver;
    private AlarmManager.AlarmClockInfo mNextAlarmInfo;
    private PendingIntent mPendingIntent;
    protected NotificationMediaManager mMediaManager;
    protected MediaMetadata mMediaMetaData;

    /**
     * Receiver responsible for time ticking and updating the date format.
@@ -104,9 +120,13 @@ public class KeyguardSliceProvider extends SliceProvider implements
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (Intent.ACTION_DATE_CHANGED.equals(action)) {
                mHandler.post(KeyguardSliceProvider.this::updateClock);
                synchronized (this) {
                    updateClockLocked();
                }
            } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
                mHandler.post(KeyguardSliceProvider.this::cleanDateFormat);
                synchronized (this) {
                    cleanDateFormatLocked();
                }
            }
        }
    };
@@ -116,12 +136,16 @@ public class KeyguardSliceProvider extends SliceProvider implements
            new KeyguardUpdateMonitorCallback() {
                @Override
                public void onTimeChanged() {
                    mHandler.post(KeyguardSliceProvider.this::updateClock);
                    synchronized (this) {
                        updateClockLocked();
                    }
                }

                @Override
                public void onTimeZoneChanged(TimeZone timeZone) {
                    mHandler.post(KeyguardSliceProvider.this::cleanDateFormat);
                    synchronized (this) {
                        cleanDateFormatLocked();
                    }
                }
            };

@@ -140,22 +164,62 @@ public class KeyguardSliceProvider extends SliceProvider implements
        mDateUri = Uri.parse(KEYGUARD_DATE_URI);
        mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
        mDndUri = Uri.parse(KEYGUARD_DND_URI);
        mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
    }

    public void initDependencies() {
        mMediaManager = Dependency.get(NotificationMediaManager.class);
        mMediaManager.addCallback(this);
    }

    @AnyThread
    @Override
    public Slice onBindSlice(Uri sliceUri) {
        Trace.beginSection("KeyguardSliceProvider#onBindSlice");
        Slice slice;
        synchronized (this) {
            ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
            if (mMediaMetaData != null) {
                addMediaLocked(builder);
            } else {
                builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
        addNextAlarm(builder);
        addZenMode(builder);
        addPrimaryAction(builder);
        Slice slice = builder.build();
                addNextAlarmLocked(builder);
                addZenModeLocked(builder);
            }
            addPrimaryActionLocked(builder);
            slice = builder.build();
        }
        Trace.endSection();
        return slice;
    }

    protected void addPrimaryAction(ListBuilder builder) {
    protected void addMediaLocked(ListBuilder listBuilder) {
        if (mMediaMetaData != null) {
            SpannableStringBuilder builder = new SpannableStringBuilder();
            CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
            if (TextUtils.isEmpty(title)) {
                title = getContext().getResources().getString(R.string.music_controls_no_title);
            }
            builder.append(title);
            builder.setSpan(BOLD_STYLE, 0, title.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

            CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
            if (!TextUtils.isEmpty(album)) {
                builder.append("  ").append(album);
            }

            RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder);
            Icon notificationIcon = mMediaManager.getMediaIcon();
            if (notificationIcon != null) {
                IconCompat icon = IconCompat.createFromIcon(notificationIcon);
                mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE);
            }

            listBuilder.addRow(mediaBuilder);
        }
    }

    protected void addPrimaryActionLocked(ListBuilder builder) {
        // Add simple action because API requires it; Keyguard handles presenting
        // its own slices so this action + icon are actually never used.
        IconCompat icon = IconCompat.createWithResource(getContext(),
@@ -167,7 +231,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
        builder.addRow(primaryActionRow);
    }

    protected void addNextAlarm(ListBuilder builder) {
    protected void addNextAlarmLocked(ListBuilder builder) {
        if (TextUtils.isEmpty(mNextAlarm)) {
            return;
        }
@@ -183,7 +247,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
     * Add zen mode (DND) icon to slice if it's enabled.
     * @param builder The slice builder.
     */
    protected void addZenMode(ListBuilder builder) {
    protected void addZenModeLocked(ListBuilder builder) {
        if (!isDndOn()) {
            return;
        }
@@ -214,22 +278,23 @@ public class KeyguardSliceProvider extends SliceProvider implements
        mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
        KeyguardSliceProvider.sInstance = this;
        registerClockUpdate();
        updateClock();
        updateClockLocked();
        return true;
    }

    @Override
    public void onZenChanged(int zen) {
        mContentResolver.notifyChange(mSliceUri, null /* observer */);
        notifyChange();
    }

    @Override
    public void onConfigChanged(ZenModeConfig config) {
        mContentResolver.notifyChange(mSliceUri, null /* observer */);
        notifyChange();
    }

    private void updateNextAlarm() {
        if (withinNHours(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) {
        synchronized (this) {
            if (withinNHoursLocked(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) {
                String pattern = android.text.format.DateFormat.is24HourFormat(getContext(),
                        ActivityManager.getCurrentUser()) ? "HH:mm" : "h:mm";
                mNextAlarm = android.text.format.DateFormat.format(pattern,
@@ -237,10 +302,11 @@ public class KeyguardSliceProvider extends SliceProvider implements
            } else {
                mNextAlarm = "";
            }
        mContentResolver.notifyChange(mSliceUri, null /* observer */);
        }
        notifyChange();
    }

    private boolean withinNHours(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) {
    private boolean withinNHoursLocked(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) {
        if (alarmClockInfo == null) {
            return false;
        }
@@ -254,6 +320,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
     * changing the date/time via the settings app.
     */
    private void registerClockUpdate() {
        synchronized (this) {
            if (mRegistered) {
                return;
            }
@@ -266,21 +333,24 @@ public class KeyguardSliceProvider extends SliceProvider implements
            getKeyguardUpdateMonitor().registerCallback(mKeyguardUpdateMonitorCallback);
            mRegistered = true;
        }
    }

    @VisibleForTesting
    boolean isRegistered() {
        synchronized (this) {
            return mRegistered;
        }
    }

    protected void updateClock() {
        final String text = getFormattedDate();
    protected void updateClockLocked() {
        final String text = getFormattedDateLocked();
        if (!text.equals(mLastText)) {
            mLastText = text;
            mContentResolver.notifyChange(mSliceUri, null /* observer */);
            notifyChange();
        }
    }

    protected String getFormattedDate() {
    protected String getFormattedDateLocked() {
        if (mDateFormat == null) {
            final Locale l = Locale.getDefault();
            DateFormat format = DateFormat.getInstanceForSkeleton(mDatePattern, l);
@@ -292,12 +362,13 @@ public class KeyguardSliceProvider extends SliceProvider implements
    }

    @VisibleForTesting
    void cleanDateFormat() {
    void cleanDateFormatLocked() {
        mDateFormat = null;
    }

    @Override
    public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
        synchronized (this) {
            mNextAlarmInfo = nextAlarm;
            mAlarmManager.cancel(mUpdateNextAlarm);

@@ -307,6 +378,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
                mAlarmManager.setExact(AlarmManager.RTC, triggerAt, "lock_screen_next_alarm",
                        mUpdateNextAlarm, mHandler);
            }
        }
        updateNextAlarm();
    }

@@ -314,4 +386,16 @@ public class KeyguardSliceProvider extends SliceProvider implements
    protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
        return KeyguardUpdateMonitor.getInstance(getContext());
    }

    @Override
    public void onMetadataChanged(MediaMetadata metadata) {
        synchronized (this) {
            mMediaMetaData = metadata;
        }
        notifyChange();
    }

    protected void notifyChange() {
        mContentResolver.notifyChange(mSliceUri, null /* observer */);
    }
}
+50 −16
Original line number Diff line number Diff line
@@ -25,11 +25,10 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -47,6 +46,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
@@ -94,14 +94,11 @@ public class NotificationMediaManager implements Dumpable {
    @Nullable
    private LockscreenWallpaper mLockscreenWallpaper;

    protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
    protected final PorterDuffXfermode mSrcOverXferMode =
            new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);

    private final Handler mHandler = Dependency.get(MAIN_HANDLER);

    private final Context mContext;
    private final MediaSessionManager mMediaSessionManager;
    private final ArrayList<MediaListener> mMediaListeners;

    protected NotificationPresenter mPresenter;
    private MediaController mMediaController;
@@ -122,7 +119,7 @@ public class NotificationMediaManager implements Dumpable {
            if (state != null) {
                if (!isPlaybackActive(state.getState())) {
                    clearCurrentMediaNotification();
                    mPresenter.updateMediaMetaData(true, true);
                    dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
                }
            }
        }
@@ -134,7 +131,7 @@ public class NotificationMediaManager implements Dumpable {
                Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
            }
            mMediaMetadata = metadata;
            mPresenter.updateMediaMetaData(true, true);
            dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
        }
    };

@@ -164,6 +161,7 @@ public class NotificationMediaManager implements Dumpable {
    @Inject
    public NotificationMediaManager(Context context) {
        mContext = context;
        mMediaListeners = new ArrayList<>();
        mMediaSessionManager
                = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
@@ -177,7 +175,7 @@ public class NotificationMediaManager implements Dumpable {
    public void onNotificationRemoved(String key) {
        if (key.equals(mMediaNotificationKey)) {
            clearCurrentMediaNotification();
            mPresenter.updateMediaMetaData(true, true);
            dispatchUpdateMediaMetaData(true /* changed */, true /* allowEnterAnimation */);
        }
    }

@@ -189,20 +187,44 @@ public class NotificationMediaManager implements Dumpable {
        return mMediaMetadata;
    }

    public Icon getMediaIcon() {
        if (mMediaNotificationKey == null) {
            return null;
        }
        NotificationEntryManager manager = getEntryManager();
        synchronized (manager.getNotificationData()) {
            Entry entry = manager.getNotificationData().get(mMediaNotificationKey);
            if (entry == null || entry.expandedIcon == null) {
                return null;
            }

            return entry.expandedIcon.getSourceIcon();
        }
    }

    public void addCallback(MediaListener callback) {
        mMediaListeners.add(callback);
        callback.onMetadataChanged(mMediaMetadata);
    }

    public void removeCallback(MediaListener callback) {
        mMediaListeners.remove(callback);
    }

    public void findAndUpdateMediaNotifications() {
        boolean metaDataChanged = false;

        NotificationEntryManager manager = getEntryManager();
        synchronized (manager.getNotificationData()) {
            ArrayList<NotificationData.Entry> activeNotifications = manager
            ArrayList<Entry> activeNotifications = manager
                    .getNotificationData().getActiveNotifications();
            final int N = activeNotifications.size();

            // Promote the media notification with a controller in 'playing' state, if any.
            NotificationData.Entry mediaNotification = null;
            Entry mediaNotification = null;
            MediaController controller = null;
            for (int i = 0; i < N; i++) {
                final NotificationData.Entry entry = activeNotifications.get(i);
                final Entry entry = activeNotifications.get(i);

                if (entry.isMediaNotification()) {
                    final MediaSession.Token token =
@@ -242,7 +264,7 @@ public class NotificationMediaManager implements Dumpable {
                            final String pkg = aController.getPackageName();

                            for (int i = 0; i < N; i++) {
                                final NotificationData.Entry entry = activeNotifications.get(i);
                                final Entry entry = activeNotifications.get(i);
                                if (entry.notification.getPackageName().equals(pkg)) {
                                    if (DEBUG_MEDIA) {
                                        Log.v(TAG, "DEBUG_MEDIA: found controller matching "
@@ -286,9 +308,7 @@ public class NotificationMediaManager implements Dumpable {
            getEntryManager().updateNotifications();
        }

        if (mPresenter != null) {
            mPresenter.updateMediaMetaData(metaDataChanged, true);
        }
        dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
    }

    public void clearCurrentMediaNotification() {
@@ -296,6 +316,16 @@ public class NotificationMediaManager implements Dumpable {
        clearCurrentMediaNotificationSession();
    }

    private void dispatchUpdateMediaMetaData(boolean changed, boolean allowEnterAnimation) {
        if (mPresenter != null) {
            mPresenter.updateMediaMetaData(changed, allowEnterAnimation);
        }
        ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners);
        for (int i = 0; i < callbacks.size(); i++) {
            callbacks.get(i).onMetadataChanged(mMediaMetadata);
        }
    }

    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.print("    mMediaSessionManager=");
@@ -552,4 +582,8 @@ public class NotificationMediaManager implements Dumpable {
            mBackdropFront.setImageDrawable(null);
        }
    };

    public interface MediaListener {
        void onMetadataChanged(MediaMetadata metadata);
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -151,6 +151,7 @@ import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.doze.LockScreenWakeUpController;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -637,6 +638,12 @@ public class StatusBar extends SystemUI implements DemoMode,
        mNavigationBarController = Dependency.get(DisplayNavigationBarController.class);
        mBubbleController = Dependency.get(BubbleController.class);
        mBubbleController.setExpandListener(mBubbleExpandListener);
        KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
        if (sliceProvider != null) {
            sliceProvider.initDependencies();
        } else {
            Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
        }

        mColorExtractor.addOnColorsChangedListener(this);
        mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
+32 −6
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;

import android.app.AlarmManager;
import android.content.ContentResolver;
import android.media.MediaMetadata;
import android.net.Uri;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
@@ -42,6 +43,7 @@ import androidx.slice.core.SliceQuery;

import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationMediaManager;

import org.junit.Assert;
import org.junit.Before;
@@ -63,6 +65,8 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
    private ContentResolver mContentResolver;
    @Mock
    private AlarmManager mAlarmManager;
    @Mock
    private NotificationMediaManager mNotificationMediaManager;
    private TestableKeyguardSliceProvider mProvider;
    private boolean mIsZenMode;

@@ -72,6 +76,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
        mIsZenMode = false;
        mProvider = new TestableKeyguardSliceProvider();
        mProvider.attachInfo(getContext(), null);
        mProvider.initDependencies();
        SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
    }

@@ -90,6 +95,16 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
        Assert.assertNotNull("Slice must provide a title.", text);
    }

    @Test
    public void onBindSlice_readsMedia() {
        MediaMetadata metadata = mock(MediaMetadata.class);
        mProvider.onMetadataChanged(metadata);
        mProvider.onBindSlice(mProvider.getUri());
        verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE));
        verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_ARTIST));
        verify(mNotificationMediaManager).getMediaIcon();
    }

    @Test
    public void cleansDateFormat() {
        mProvider.mKeyguardUpdateMonitorCallback.onTimeZoneChanged(null);
@@ -136,14 +151,20 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
    public void addZenMode_addedToSlice() {
        ListBuilder listBuilder = spy(new ListBuilder(getContext(), mProvider.getUri(),
            ListBuilder.INFINITY));
        mProvider.addZenMode(listBuilder);
        mProvider.addZenModeLocked(listBuilder);
        verify(listBuilder, never()).addRow(any(ListBuilder.RowBuilder.class));

        mIsZenMode = true;
        mProvider.addZenMode(listBuilder);
        mProvider.addZenModeLocked(listBuilder);
        verify(listBuilder).addRow(any(ListBuilder.RowBuilder.class));
    }

    @Test
    public void onMetadataChanged_updatesSlice() {
        mProvider.onMetadataChanged(mock(MediaMetadata.class));
        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
    }

    private class TestableKeyguardSliceProvider extends KeyguardSliceProvider {
        int mCleanDateFormatInvokations;
        private int mCounter;
@@ -166,8 +187,8 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
        }

        @Override
        void cleanDateFormat() {
            super.cleanDateFormat();
        void cleanDateFormatLocked() {
            super.cleanDateFormatLocked();
            mCleanDateFormatInvokations++;
        }

@@ -177,8 +198,13 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
        }

        @Override
        protected String getFormattedDate() {
            return super.getFormattedDate() + mCounter++;
        protected String getFormattedDateLocked() {
            return super.getFormattedDateLocked() + mCounter++;
        }

        @Override
        public void initDependencies() {
            mMediaManager = mNotificationMediaManager;
        }
    }