Merge "Fix public attribute name of fetch strategy." into oc-support-26.0-dev
diff --git a/api/26.0.0-SNAPSHOT.txt b/api/26.0.0-SNAPSHOT.txt
index 036ee52..0311835 100644
--- a/api/26.0.0-SNAPSHOT.txt
+++ b/api/26.0.0-SNAPSHOT.txt
@@ -1895,6 +1895,7 @@
method public java.lang.CharSequence process(java.lang.CharSequence);
method public java.lang.CharSequence process(java.lang.CharSequence, int, int);
method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int);
+ method public java.lang.CharSequence process(java.lang.CharSequence, int, int, int, int);
method public void registerInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
method public void unregisterInitCallback(android.support.text.emoji.EmojiCompat.InitCallback);
field public static final java.lang.String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
@@ -1902,6 +1903,9 @@
field public static final int LOAD_STATE_FAILURE = 2; // 0x2
field public static final int LOAD_STATE_LOADING = 0; // 0x0
field public static final int LOAD_STATE_SUCCESS = 1; // 0x1
+ field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+ field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+ field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
}
public static abstract class EmojiCompat.Config {
diff --git a/emoji/core/src/android/support/text/emoji/EmojiCompat.java b/emoji/core/src/android/support/text/emoji/EmojiCompat.java
index 671ea78..b5db6f6 100644
--- a/emoji/core/src/android/support/text/emoji/EmojiCompat.java
+++ b/emoji/core/src/android/support/text/emoji/EmojiCompat.java
@@ -118,6 +118,30 @@
public @interface LoadState {
}
+ /**
+ * Replace strategy that uses the value given in {@link EmojiCompat.Config}.
+ */
+ public static final int REPLACE_STRATEGY_DEFAULT = 0;
+
+ /**
+ * Replace strategy to add {@link EmojiSpan}s for all emoji that were found.
+ */
+ public static final int REPLACE_STRATEGY_ALL = 1;
+
+ /**
+ * Replace strategy to add {@link EmojiSpan}s only for emoji that do not exist in the system.
+ */
+ public static final int REPLACE_STRATEGY_NON_EXISTENT = 2;
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ @IntDef({REPLACE_STRATEGY_DEFAULT, REPLACE_STRATEGY_NON_EXISTENT, REPLACE_STRATEGY_ALL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReplaceStrategy {
+ }
+
private static final Object sInstanceLock = new Object();
@GuardedBy("sInstanceLock")
@@ -231,6 +255,17 @@
}
/**
+ * Used by the tests to set GlyphChecker for EmojiProcessor.
+ *
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ @VisibleForTesting
+ void setGlyphChecker(@NonNull final EmojiProcessor.GlyphChecker glyphChecker) {
+ mHelper.setGlyphChecker(glyphChecker);
+ }
+
+ /**
* Return singleton EmojiCompat instance. Should be called after
* {@link #init(EmojiCompat.Config)} is called to initialize the singleton instance.
*
@@ -543,6 +578,45 @@
public CharSequence process(@NonNull final CharSequence charSequence,
@IntRange(from = 0) final int start, @IntRange(from = 0) final int end,
@IntRange(from = 0) final int maxEmojiCount) {
+ return process(charSequence, start, end, maxEmojiCount, REPLACE_STRATEGY_DEFAULT);
+ }
+
+ /**
+ * Checks a given CharSequence for emojis, and adds EmojiSpans if any emojis are found.
+ * <p>
+ * <ul>
+ * <li>If no emojis are found, {@code charSequence} given as the input is returned without
+ * any changes. i.e. charSequence is a String, and no emojis are found, the same String is
+ * returned.</li>
+ * <li>If the given input is not a Spannable (such as String), and at least one emoji is found
+ * a new {@link android.text.Spannable} instance is returned. </li>
+ * <li>If the given input is a Spannable, the same instance is returned. </li>
+ * </ul>
+ * When used on devices running API 18 or below, returns the given {@code charSequence} without
+ * processing it.
+ *
+ * @param charSequence CharSequence to add the EmojiSpans, cannot be {@code null}
+ * @param start start index in the charSequence to look for emojis, should be greater than or
+ * equal to {@code 0}, also less than {@code charSequence.length()}
+ * @param end end index in the charSequence to look for emojis, should be greater than or
+ * equal to {@code start} parameter, also less than {@code charSequence.length()}
+ * @param maxEmojiCount maximum number of emojis in the {@code charSequence}, should be greater
+ * than or equal to {@code 0}
+ * @param replaceStrategy whether to replace all emoji with {@link EmojiSpan}s, should be one of
+ * {@link #REPLACE_STRATEGY_DEFAULT},
+ * {@link #REPLACE_STRATEGY_NON_EXISTENT},
+ * {@link #REPLACE_STRATEGY_ALL}
+ *
+ * @throws IllegalStateException if not initialized yet
+ * @throws IllegalArgumentException in the following cases:
+ * {@code start < 0}, {@code end < 0}, {@code end < start},
+ * {@code start > charSequence.length()},
+ * {@code end > charSequence.length()}
+ * {@code maxEmojiCount < 0}
+ */
+ public CharSequence process(@NonNull final CharSequence charSequence,
+ @IntRange(from = 0) final int start, @IntRange(from = 0) final int end,
+ @IntRange(from = 0) final int maxEmojiCount, @ReplaceStrategy int replaceStrategy) {
Preconditions.checkState(isInitialized(), "Not initialized yet");
Preconditions.checkArgumentNonnegative(start, "start cannot be negative");
Preconditions.checkArgumentNonnegative(end, "end cannot be negative");
@@ -564,7 +638,21 @@
return charSequence;
}
- return mHelper.process(charSequence, start, end, maxEmojiCount);
+ final boolean replaceAll;
+ switch (replaceStrategy) {
+ case REPLACE_STRATEGY_ALL:
+ replaceAll = true;
+ break;
+ case REPLACE_STRATEGY_NON_EXISTENT:
+ replaceAll = false;
+ break;
+ case REPLACE_STRATEGY_DEFAULT:
+ default:
+ replaceAll = mReplaceAll;
+ break;
+ }
+
+ return mHelper.process(charSequence, start, end, maxEmojiCount, replaceAll);
}
/**
@@ -838,7 +926,7 @@
CharSequence process(@NonNull final CharSequence charSequence,
@IntRange(from = 0) final int start, @IntRange(from = 0) final int end,
- @IntRange(from = 0) final int maxEmojiCount) {
+ @IntRange(from = 0) final int maxEmojiCount, boolean replaceAll) {
// Returns the given charSequence as it is.
return charSequence;
}
@@ -846,6 +934,10 @@
void updateEditorInfoAttrs(@NonNull final EditorInfo outAttrs) {
// Does not add any EditorInfo attributes.
}
+
+ void setGlyphChecker(@NonNull EmojiProcessor.GlyphChecker glyphChecker) {
+ // intentionally empty
+ }
}
@RequiresApi(19)
@@ -893,8 +985,7 @@
}
mMetadataRepo = metadataRepo;
- mProcessor = new EmojiProcessor(mMetadataRepo, new SpanFactory(),
- mEmojiCompat.mReplaceAll);
+ mProcessor = new EmojiProcessor(mMetadataRepo, new SpanFactory());
mEmojiCompat.onMetadataLoadSuccess();
}
@@ -912,8 +1003,8 @@
@Override
CharSequence process(@NonNull CharSequence charSequence, int start, int end,
- int maxEmojiCount) {
- return mProcessor.process(charSequence, start, end, maxEmojiCount);
+ int maxEmojiCount, boolean replaceAll) {
+ return mProcessor.process(charSequence, start, end, maxEmojiCount, replaceAll);
}
@Override
@@ -921,5 +1012,10 @@
outAttrs.extras.putInt(EDITOR_INFO_METAVERSION_KEY, mMetadataRepo.getMetadataVersion());
outAttrs.extras.putBoolean(EDITOR_INFO_REPLACE_ALL_KEY, mEmojiCompat.mReplaceAll);
}
+
+ @Override
+ void setGlyphChecker(@NonNull EmojiProcessor.GlyphChecker glyphChecker) {
+ mProcessor.setGlyphChecker(glyphChecker);
+ }
}
}
diff --git a/emoji/core/src/android/support/text/emoji/EmojiProcessor.java b/emoji/core/src/android/support/text/emoji/EmojiProcessor.java
index b51a698..f168e59 100644
--- a/emoji/core/src/android/support/text/emoji/EmojiProcessor.java
+++ b/emoji/core/src/android/support/text/emoji/EmojiProcessor.java
@@ -26,6 +26,7 @@
import android.support.annotation.RestrictTo;
import android.support.text.emoji.widget.SpannableBuilder;
import android.support.v4.graphics.PaintCompat;
+import android.support.v4.util.Preconditions;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
@@ -75,17 +76,6 @@
private static final int ACTION_FLUSH = 3;
/**
- * Default text size for {@link #mTextPaint}.
- */
- private static final int PAINT_TEXT_SIZE = 10;
-
- /**
- * Used to create strings required by
- * {@link PaintCompat#hasGlyph(android.graphics.Paint, String)}.
- */
- private static final ThreadLocal<StringBuilder> sStringBuilder = new ThreadLocal<>();
-
- /**
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@@ -97,28 +87,19 @@
private final EmojiCompat.SpanFactory mSpanFactory;
/**
- * @see EmojiCompat.Config#setReplaceAll(boolean)
- */
- private final boolean mReplaceAll;
-
- /**
* Emoji metadata repository.
*/
private final MetadataRepo mMetadataRepo;
/**
- * TextPaint used during {@link PaintCompat#hasGlyph(android.graphics.Paint, String)} check.
+ * Utility class that checks if the system can render a given glyph.
*/
- private final TextPaint mTextPaint;
+ private GlyphChecker mGlyphChecker = new GlyphChecker();
EmojiProcessor(@NonNull final MetadataRepo metadataRepo,
- @NonNull final EmojiCompat.SpanFactory spanFactory,
- final boolean replaceAll) {
+ @NonNull final EmojiCompat.SpanFactory spanFactory) {
mSpanFactory = spanFactory;
mMetadataRepo = metadataRepo;
- mReplaceAll = replaceAll;
- mTextPaint = new TextPaint();
- mTextPaint.setTextSize(PAINT_TEXT_SIZE);
}
EmojiMetadata getEmojiMetadata(@NonNull final CharSequence charSequence) {
@@ -161,9 +142,11 @@
* equal to {@code start} parameter, also less than {@code charSequence.length()}
* @param maxEmojiCount maximum number of emojis in the {@code charSequence}, should be greater
* than or equal to {@code 0}
+ * @param replaceAll whether to replace all emoji with {@link EmojiSpan}s
*/
CharSequence process(@NonNull final CharSequence charSequence, @IntRange(from = 0) int start,
- @IntRange(from = 0) int end, @IntRange(from = 0) int maxEmojiCount) {
+ @IntRange(from = 0) int end, @IntRange(from = 0) int maxEmojiCount,
+ final boolean replaceAll) {
final boolean isSpannableBuilder = charSequence instanceof SpannableBuilder;
if (isSpannableBuilder) {
((SpannableBuilder) charSequence).beginBatchEdit();
@@ -235,7 +218,7 @@
}
break;
case ACTION_FLUSH:
- if (mReplaceAll || !hasGlyph(charSequence, start, currentOffset,
+ if (replaceAll || !hasGlyph(charSequence, start, currentOffset,
sm.getFlushMetadata())) {
if (spannable == null) {
spannable = new SpannableString(charSequence);
@@ -253,7 +236,7 @@
// state machine is waiting to see if there is an emoji sequence (i.e. ZWJ).
// Need to check if it is in such a state.
if (sm.isInFlushableState() && addedCount < maxEmojiCount) {
- if (mReplaceAll || !hasGlyph(charSequence, start, currentOffset,
+ if (replaceAll || !hasGlyph(charSequence, start, currentOffset,
sm.getCurrentMetadata())) {
if (spannable == null) {
spannable = new SpannableString(charSequence);
@@ -454,26 +437,19 @@
// if the existence is not calculated yet
if (metadata.getHasGlyph() == EmojiMetadata.HAS_GLYPH_UNKNOWN) {
- final StringBuilder builder = getStringBuilder();
- builder.setLength(0);
-
- while (start < end) {
- builder.append(charSequence.charAt(start));
- start++;
- }
-
- final boolean hasGlyph = PaintCompat.hasGlyph(mTextPaint, builder.toString());
+ final boolean hasGlyph = mGlyphChecker.hasGlyph(charSequence, start, end);
metadata.setHasGlyph(hasGlyph);
}
return metadata.getHasGlyph() == EmojiMetadata.HAS_GLYPH_EXISTS;
}
- private static StringBuilder getStringBuilder() {
- if (sStringBuilder.get() == null) {
- sStringBuilder.set(new StringBuilder());
- }
- return sStringBuilder.get();
+ /**
+ * Set the GlyphChecker instance used by EmojiProcessor. Used for testing.
+ */
+ void setGlyphChecker(@NonNull final GlyphChecker glyphChecker) {
+ Preconditions.checkNotNull(glyphChecker);
+ mGlyphChecker = glyphChecker;
}
/**
@@ -743,4 +719,63 @@
}
}
}
+
+ /**
+ * Utility class that checks if the system can render a given glyph.
+ *
+ * @hide
+ */
+ @AnyThread
+ @RestrictTo(LIBRARY_GROUP)
+ public static class GlyphChecker {
+ /**
+ * Default text size for {@link #mTextPaint}.
+ */
+ private static final int PAINT_TEXT_SIZE = 10;
+
+ /**
+ * Used to create strings required by
+ * {@link PaintCompat#hasGlyph(android.graphics.Paint, String)}.
+ */
+ private static final ThreadLocal<StringBuilder> sStringBuilder = new ThreadLocal<>();
+
+ /**
+ * TextPaint used during {@link PaintCompat#hasGlyph(android.graphics.Paint, String)} check.
+ */
+ private final TextPaint mTextPaint;
+
+ GlyphChecker() {
+ mTextPaint = new TextPaint();
+ mTextPaint.setTextSize(PAINT_TEXT_SIZE);
+ }
+
+ /**
+ * Returns whether the system can render an emoji.
+ *
+ * @param charSequence the CharSequence that the emoji is in
+ * @param start start index of the emoji in the CharSequence
+ * @param end end index of the emoji in the CharSequence
+ *
+ * @return {@code true} if the OS can render emoji, {@code false} otherwise
+ */
+ public boolean hasGlyph(final CharSequence charSequence, int start, final int end) {
+ final StringBuilder builder = getStringBuilder();
+ builder.setLength(0);
+
+ while (start < end) {
+ builder.append(charSequence.charAt(start));
+ start++;
+ }
+
+ return PaintCompat.hasGlyph(mTextPaint, builder.toString());
+ }
+
+ private static StringBuilder getStringBuilder() {
+ if (sStringBuilder.get() == null) {
+ sStringBuilder.set(new StringBuilder());
+ }
+ return sStringBuilder.get();
+ }
+
+ }
}
diff --git a/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java b/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
index 00df303..29964ce 100644
--- a/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
@@ -54,10 +54,15 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.annotation.SuppressLint;
import android.os.Bundle;
@@ -479,6 +484,50 @@
original.length()));
}
+ @Test
+ @SdkSuppress(minSdkVersion = 19)
+ public void testProcess_withReplaceNonExistent_callsGlyphChecker() {
+ final Config config = TestConfigBuilder.config().setReplaceAll(true);
+ EmojiCompat.reset(config);
+
+ final EmojiProcessor.GlyphChecker glyphChecker = mock(EmojiProcessor.GlyphChecker.class);
+ when(glyphChecker.hasGlyph(any(CharSequence.class), anyInt(), anyInt())).thenReturn(true);
+ EmojiCompat.get().setGlyphChecker(glyphChecker);
+
+ final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString();
+
+ CharSequence processed = EmojiCompat.get().process(original, 0, original.length(),
+ Integer.MAX_VALUE /*maxEmojiCount*/, EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT);
+
+ // when function overrides config level replaceAll, a call to GlyphChecker is expected.
+ verify(glyphChecker, times(1)).hasGlyph(any(CharSequence.class), anyInt(), anyInt());
+
+ // since replaceAll is false, there should be no EmojiSpans
+ assertThat(processed, not(hasEmoji()));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 19)
+ public void testProcess_withReplaceDefault_doesNotCallGlyphChecker() {
+ final Config config = TestConfigBuilder.config().setReplaceAll(true);
+ EmojiCompat.reset(config);
+
+ final EmojiProcessor.GlyphChecker glyphChecker = mock(EmojiProcessor.GlyphChecker.class);
+ when(glyphChecker.hasGlyph(any(CharSequence.class), anyInt(), anyInt())).thenReturn(true);
+ EmojiCompat.get().setGlyphChecker(glyphChecker);
+
+ final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString();
+ // call without replaceAll, config value (true) should be used
+ final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(),
+ Integer.MAX_VALUE /*maxEmojiCount*/, EmojiCompat.REPLACE_STRATEGY_DEFAULT);
+
+ // replaceAll=true should not call hasGlyph
+ verify(glyphChecker, times(0)).hasGlyph(any(CharSequence.class), anyInt(), anyInt());
+
+ assertThat(processed, hasEmojiCount(1));
+ assertThat(processed, hasEmoji(EMOJI_SINGLE_CODEPOINT));
+ }
+
@Test(expected = NullPointerException.class)
public void testHasEmojiGlyph_withNullCharSequence() {
EmojiCompat.get().hasEmojiGlyph(null);
@@ -682,6 +731,4 @@
charSequence = EmojiCompat.get().process(string.toString());
assertThat(charSequence, not(hasEmoji()));
}
-
- //FAILS: CHAR_DIGIT, CHAR_VS_EMOJI, CHAR_VS_TEXT
}
diff --git a/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java b/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
index 7d0aa5d..ab21eda 100644
--- a/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
@@ -28,6 +28,7 @@
import android.content.ComponentName;
import android.os.Bundle;
import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.testutils.PollingCheck;
@@ -158,15 +159,24 @@
}
@Test
- @LargeTest
+ @SmallTest
public void testReconnection() throws Exception {
createMediaBrowser(TEST_BROWSER_SERVICE);
- // Reconnect before the first connection was established.
- mMediaBrowser.connect();
- mMediaBrowser.disconnect();
- resetCallbacks();
- connectMediaBrowserService();
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mMediaBrowser.connect();
+ // Reconnect before the first connection was established.
+ mMediaBrowser.disconnect();
+ mMediaBrowser.connect();
+ }
+ });
+
+ synchronized (mConnectionCallback.mWaitLock) {
+ mConnectionCallback.mWaitLock.wait(TIME_OUT_MS);
+ assertEquals(1, mConnectionCallback.mConnectedCount);
+ }
synchronized (mSubscriptionCallback.mWaitLock) {
// Test subscribe.
@@ -206,12 +216,19 @@
}
@Test
- @LargeTest
+ @SmallTest
public void testConnectionCallbackNotCalledAfterDisconnect() {
createMediaBrowser(TEST_BROWSER_SERVICE);
- mMediaBrowser.connect();
- mMediaBrowser.disconnect();
- resetCallbacks();
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mMediaBrowser.connect();
+ mMediaBrowser.disconnect();
+ resetCallbacks();
+ }
+ });
+
try {
Thread.sleep(SLEEP_MS);
} catch (InterruptedException e) {
@@ -370,29 +387,28 @@
}
@Test
- @LargeTest
+ @SmallTest
public void testUnsubscribeForMultipleSubscriptions() throws Exception {
createMediaBrowser(TEST_BROWSER_SERVICE);
connectMediaBrowserService();
final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
final int pageSize = 1;
- synchronized (mSubscriptionCallback.mWaitLock) {
- // Subscribe four pages, one item per page.
- for (int page = 0; page < 4; page++) {
- final StubSubscriptionCallback callback = new StubSubscriptionCallback();
- subscriptionCallbacks.add(callback);
+ // Subscribe four pages, one item per page.
+ for (int page = 0; page < 4; page++) {
+ final StubSubscriptionCallback callback = new StubSubscriptionCallback();
+ subscriptionCallbacks.add(callback);
- Bundle options = new Bundle();
- options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
- options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
- mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options,
- callback);
- mSubscriptionCallback.mWaitLock.wait(TIME_OUT_MS);
-
- // Each onChildrenLoaded() must be called.
- assertEquals(1, callback.mChildrenLoadedWithOptionCount);
+ Bundle options = new Bundle();
+ options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
+ options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
+ mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options,
+ callback);
+ synchronized (callback.mWaitLock) {
+ callback.mWaitLock.wait(TIME_OUT_MS);
}
+ // Each onChildrenLoaded() must be called.
+ assertEquals(1, callback.mChildrenLoadedWithOptionCount);
}
// Reset callbacks and unsubscribe.
@@ -418,29 +434,28 @@
}
@Test
- @LargeTest
+ @MediumTest
public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() throws Exception {
createMediaBrowser(TEST_BROWSER_SERVICE);
connectMediaBrowserService();
final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
final int pageSize = 1;
- synchronized (mSubscriptionCallback.mWaitLock) {
- // Subscribe four pages, one item per page.
- for (int page = 0; page < 4; page++) {
- final StubSubscriptionCallback callback = new StubSubscriptionCallback();
- subscriptionCallbacks.add(callback);
+ // Subscribe four pages, one item per page.
+ for (int page = 0; page < 4; page++) {
+ final StubSubscriptionCallback callback = new StubSubscriptionCallback();
+ subscriptionCallbacks.add(callback);
- Bundle options = new Bundle();
- options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
- options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
- mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options,
- callback);
- mSubscriptionCallback.mWaitLock.wait(TIME_OUT_MS);
-
- // Each onChildrenLoaded() must be called.
- assertEquals(1, callback.mChildrenLoadedWithOptionCount);
+ Bundle options = new Bundle();
+ options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
+ options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
+ mMediaBrowser.subscribe(StubMediaBrowserServiceCompat.MEDIA_ID_ROOT, options,
+ callback);
+ synchronized (callback.mWaitLock) {
+ callback.mWaitLock.wait(TIME_OUT_MS);
}
+ // Each onChildrenLoaded() must be called.
+ assertEquals(1, callback.mChildrenLoadedWithOptionCount);
}
// Unsubscribe existing subscriptions one-by-one.