1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.data; 18 19 import android.content.ContentProvider; 20 import android.content.ContentUris; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.database.ContentObserver; 24 import android.database.Cursor; 25 import android.media.tv.TvContract; 26 import android.media.tv.TvContract.Channels; 27 import android.net.Uri; 28 import android.support.test.filters.SmallTest; 29 import android.test.AndroidTestCase; 30 import android.test.MoreAsserts; 31 import android.test.UiThreadTest; 32 import android.test.mock.MockContentProvider; 33 import android.test.mock.MockContentResolver; 34 import android.test.mock.MockCursor; 35 import android.text.TextUtils; 36 import android.util.Log; 37 import android.util.SparseArray; 38 39 import com.android.tv.testing.ChannelInfo; 40 import com.android.tv.testing.Constants; 41 import com.android.tv.testing.Utils; 42 import com.android.tv.util.TvInputManagerHelper; 43 44 import org.mockito.Matchers; 45 import org.mockito.Mockito; 46 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 import java.util.List; 50 import java.util.concurrent.CountDownLatch; 51 import java.util.concurrent.TimeUnit; 52 53 /** 54 * Test for {@link ChannelDataManager} 55 * 56 * A test method may include tests for multiple methods to minimize the DB access. 57 * Note that all the methods of {@link ChannelDataManager} should be called from the UI thread. 58 */ 59 @SmallTest 60 public class ChannelDataManagerTest extends AndroidTestCase { 61 private static final boolean DEBUG = false; 62 private static final String TAG = "ChannelDataManagerTest"; 63 64 // Wait time for expected success. 65 private static final long WAIT_TIME_OUT_MS = 1000L; 66 private static final String DUMMY_INPUT_ID = "dummy"; 67 // TODO: Use Channels.COLUMN_BROWSABLE and Channels.COLUMN_LOCKED instead. 68 private static final String COLUMN_BROWSABLE = "browsable"; 69 private static final String COLUMN_LOCKED = "locked"; 70 71 private ChannelDataManager mChannelDataManager; 72 private TestChannelDataManagerListener mListener; 73 private FakeContentResolver mContentResolver; 74 private FakeContentProvider mContentProvider; 75 76 @Override setUp()77 protected void setUp() throws Exception { 78 super.setUp(); 79 assertTrue("More than 2 channels to test", Constants.UNIT_TEST_CHANNEL_COUNT > 2); 80 81 mContentProvider = new FakeContentProvider(getContext()); 82 mContentResolver = new FakeContentResolver(); 83 mContentResolver.addProvider(TvContract.AUTHORITY, mContentProvider); 84 mListener = new TestChannelDataManagerListener(); 85 Utils.runOnMainSync(new Runnable() { 86 @Override 87 public void run() { 88 TvInputManagerHelper mockHelper = Mockito.mock(TvInputManagerHelper.class); 89 Mockito.when(mockHelper.hasTvInputInfo(Matchers.anyString())).thenReturn(true); 90 mChannelDataManager = new ChannelDataManager(getContext(), mockHelper, 91 mContentResolver); 92 mChannelDataManager.addListener(mListener); 93 } 94 }); 95 } 96 97 @Override tearDown()98 protected void tearDown() throws Exception { 99 Utils.runOnMainSync(new Runnable() { 100 @Override 101 public void run() { 102 mChannelDataManager.stop(); 103 } 104 }); 105 super.tearDown(); 106 } 107 startAndWaitForComplete()108 private void startAndWaitForComplete() throws Exception { 109 mChannelDataManager.start(); 110 assertTrue(mListener.loadFinishedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); 111 } 112 restart()113 private void restart() throws Exception { 114 mChannelDataManager.stop(); 115 mListener.reset(); 116 startAndWaitForComplete(); 117 } 118 119 @UiThreadTest testIsDbLoadFinished()120 public void testIsDbLoadFinished() throws Exception { 121 startAndWaitForComplete(); 122 assertTrue(mChannelDataManager.isDbLoadFinished()); 123 } 124 125 /** 126 * Test for following methods 127 * - {@link ChannelDataManager#getChannelCount} 128 * - {@link ChannelDataManager#getChannelList} 129 * - {@link ChannelDataManager#getChannel} 130 */ 131 @UiThreadTest testGetChannels()132 public void testGetChannels() throws Exception { 133 startAndWaitForComplete(); 134 135 // Test {@link ChannelDataManager#getChannelCount} 136 assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT, mChannelDataManager.getChannelCount()); 137 138 // Test {@link ChannelDataManager#getChannelList} 139 List<ChannelInfo> channelInfoList = new ArrayList<>(); 140 for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) { 141 channelInfoList.add(ChannelInfo.create(getContext(), i)); 142 } 143 List<Channel> channelList = mChannelDataManager.getChannelList(); 144 for (Channel channel : channelList) { 145 boolean found = false; 146 for (ChannelInfo channelInfo : channelInfoList) { 147 if (TextUtils.equals(channelInfo.name, channel.getDisplayName()) 148 && TextUtils.equals(channelInfo.name, channel.getDisplayName())) { 149 found = true; 150 channelInfoList.remove(channelInfo); 151 break; 152 } 153 } 154 assertTrue("Cannot find (" + channel + ")", found); 155 } 156 157 // Test {@link ChannelDataManager#getChannelIndex()} 158 for (Channel channel : channelList) { 159 assertEquals(channel, mChannelDataManager.getChannel(channel.getId())); 160 } 161 } 162 163 /** 164 * Test for {@link ChannelDataManager#getChannelCount} when no channel is available. 165 */ 166 @UiThreadTest testGetChannels_noChannels()167 public void testGetChannels_noChannels() throws Exception { 168 mContentProvider.clear(); 169 startAndWaitForComplete(); 170 assertEquals(0, mChannelDataManager.getChannelCount()); 171 } 172 173 /** 174 * Test for following methods and channel listener with notifying change. 175 * - {@link ChannelDataManager#updateBrowsable} 176 * - {@link ChannelDataManager#applyUpdatedValuesToDb} 177 */ 178 @UiThreadTest testBrowsable()179 public void testBrowsable() throws Exception { 180 startAndWaitForComplete(); 181 182 // Test if all channels are browsable 183 List<Channel> channelList = new ArrayList<>(mChannelDataManager.getChannelList()); 184 List<Channel> browsableChannelList = mChannelDataManager.getBrowsableChannelList(); 185 for (Channel browsableChannel : browsableChannelList) { 186 boolean found = channelList.remove(browsableChannel); 187 assertTrue("Cannot find (" + browsableChannel + ")", found); 188 } 189 assertEquals(0, channelList.size()); 190 191 // Prepare for next tests. 192 TestChannelDataManagerChannelListener channelListener = 193 new TestChannelDataManagerChannelListener(); 194 Channel channel1 = mChannelDataManager.getChannelList().get(0); 195 mChannelDataManager.addChannelListener(channel1.getId(), channelListener); 196 197 // Test {@link ChannelDataManager#updateBrowsable} & notification. 198 mChannelDataManager.updateBrowsable(channel1.getId(), false, false); 199 assertTrue(mListener.channelBrowsableChangedCalled); 200 assertFalse(mChannelDataManager.getBrowsableChannelList().contains(channel1)); 201 MoreAsserts.assertContentsInAnyOrder(channelListener.updatedChannels, channel1); 202 channelListener.reset(); 203 204 // Test {@link ChannelDataManager#applyUpdatedValuesToDb} 205 // Disable the update notification to avoid the unwanted call of "onLoadFinished". 206 mContentResolver.mNotifyDisabled = true; 207 mChannelDataManager.applyUpdatedValuesToDb(); 208 restart(); 209 browsableChannelList = mChannelDataManager.getBrowsableChannelList(); 210 assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT - 1, browsableChannelList.size()); 211 assertFalse(browsableChannelList.contains(channel1)); 212 } 213 214 /** 215 * Test for following methods and channel listener without notifying change. 216 * - {@link ChannelDataManager#updateBrowsable} 217 * - {@link ChannelDataManager#applyUpdatedValuesToDb} 218 */ 219 @UiThreadTest testBrowsable_skipNotification()220 public void testBrowsable_skipNotification() throws Exception { 221 startAndWaitForComplete(); 222 223 // Prepare for next tests. 224 TestChannelDataManagerChannelListener channelListener = 225 new TestChannelDataManagerChannelListener(); 226 Channel channel1 = mChannelDataManager.getChannelList().get(0); 227 Channel channel2 = mChannelDataManager.getChannelList().get(1); 228 mChannelDataManager.addChannelListener(channel1.getId(), channelListener); 229 mChannelDataManager.addChannelListener(channel2.getId(), channelListener); 230 231 // Test {@link ChannelDataManager#updateBrowsable} & skip notification. 232 mChannelDataManager.updateBrowsable(channel1.getId(), false, true); 233 mChannelDataManager.updateBrowsable(channel2.getId(), false, true); 234 mChannelDataManager.updateBrowsable(channel1.getId(), true, true); 235 assertFalse(mListener.channelBrowsableChangedCalled); 236 List<Channel> browsableChannelList = mChannelDataManager.getBrowsableChannelList(); 237 assertTrue(browsableChannelList.contains(channel1)); 238 assertFalse(browsableChannelList.contains(channel2)); 239 240 // Test {@link ChannelDataManager#applyUpdatedValuesToDb} 241 // Disable the update notification to avoid the unwanted call of "onLoadFinished". 242 mContentResolver.mNotifyDisabled = true; 243 mChannelDataManager.applyUpdatedValuesToDb(); 244 restart(); 245 browsableChannelList = mChannelDataManager.getBrowsableChannelList(); 246 assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT - 1, browsableChannelList.size()); 247 assertFalse(browsableChannelList.contains(channel2)); 248 } 249 250 /** 251 * Test for following methods and channel listener. 252 * - {@link ChannelDataManager#updateLocked} 253 * - {@link ChannelDataManager#applyUpdatedValuesToDb} 254 */ 255 @UiThreadTest testLocked()256 public void testLocked() throws Exception { 257 startAndWaitForComplete(); 258 259 // Test if all channels aren't locked at the first time. 260 List<Channel> channelList = mChannelDataManager.getChannelList(); 261 for (Channel channel : channelList) { 262 assertFalse(channel + " is locked", channel.isLocked()); 263 } 264 265 // Prepare for next tests. 266 Channel channel = mChannelDataManager.getChannelList().get(0); 267 268 // Test {@link ChannelDataManager#updateLocked} 269 mChannelDataManager.updateLocked(channel.getId(), true); 270 assertTrue(mChannelDataManager.getChannel(channel.getId()).isLocked()); 271 272 // Test {@link ChannelDataManager#applyUpdatedValuesToDb}. 273 // Disable the update notification to avoid the unwanted call of "onLoadFinished". 274 mContentResolver.mNotifyDisabled = true; 275 mChannelDataManager.applyUpdatedValuesToDb(); 276 restart(); 277 assertTrue(mChannelDataManager.getChannel(channel.getId()).isLocked()); 278 279 // Cleanup 280 mChannelDataManager.updateLocked(channel.getId(), false); 281 } 282 283 /** 284 * Test ChannelDataManager when channels in TvContract are updated, removed, or added. 285 */ 286 @UiThreadTest testChannelListChanged()287 public void testChannelListChanged() throws Exception { 288 startAndWaitForComplete(); 289 290 // Test channel add. 291 mListener.reset(); 292 long testChannelId = Constants.UNIT_TEST_CHANNEL_COUNT + 1; 293 ChannelInfo testChannelInfo = ChannelInfo.create(getContext(), (int) testChannelId); 294 testChannelId = Constants.UNIT_TEST_CHANNEL_COUNT + 1; 295 mContentProvider.simulateInsert(testChannelInfo); 296 assertTrue( 297 mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); 298 assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT + 1, mChannelDataManager.getChannelCount()); 299 300 // Test channel update 301 mListener.reset(); 302 TestChannelDataManagerChannelListener channelListener = 303 new TestChannelDataManagerChannelListener(); 304 mChannelDataManager.addChannelListener(testChannelId, channelListener); 305 String newName = testChannelInfo.name + "_test"; 306 mContentProvider.simulateUpdate(testChannelId, newName); 307 assertTrue( 308 mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); 309 assertTrue( 310 channelListener.channelChangedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); 311 assertEquals(0, channelListener.removedChannels.size()); 312 assertEquals(1, channelListener.updatedChannels.size()); 313 Channel updatedChannel = channelListener.updatedChannels.get(0); 314 assertEquals(testChannelId, updatedChannel.getId()); 315 assertEquals(testChannelInfo.number, updatedChannel.getDisplayNumber()); 316 assertEquals(newName, updatedChannel.getDisplayName()); 317 assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT + 1, 318 mChannelDataManager.getChannelCount()); 319 320 // Test channel remove. 321 mListener.reset(); 322 channelListener.reset(); 323 mContentProvider.simulateDelete(testChannelId); 324 assertTrue( 325 mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); 326 assertTrue( 327 channelListener.channelChangedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); 328 assertEquals(1, channelListener.removedChannels.size()); 329 assertEquals(0, channelListener.updatedChannels.size()); 330 Channel removedChannel = channelListener.removedChannels.get(0); 331 assertEquals(newName, removedChannel.getDisplayName()); 332 assertEquals(testChannelInfo.number, removedChannel.getDisplayNumber()); 333 assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT, mChannelDataManager.getChannelCount()); 334 } 335 336 private class ChannelInfoWrapper { 337 public ChannelInfo channelInfo; 338 public boolean browsable; 339 public boolean locked; ChannelInfoWrapper(ChannelInfo channelInfo)340 public ChannelInfoWrapper(ChannelInfo channelInfo) { 341 this.channelInfo = channelInfo; 342 browsable = true; 343 locked = false; 344 } 345 } 346 347 private class FakeContentResolver extends MockContentResolver { 348 boolean mNotifyDisabled; 349 350 @Override notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork)351 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { 352 super.notifyChange(uri, observer, syncToNetwork); 353 if (DEBUG) { 354 Log.d(TAG, "onChanged(uri=" + uri + ", observer=" + observer + ") - Notification " 355 + (mNotifyDisabled ? "disabled" : "enabled")); 356 } 357 if (mNotifyDisabled) { 358 return; 359 } 360 // Do not call {@link ContentObserver#onChange} directly to run it on the correct 361 // thread. 362 if (observer != null) { 363 observer.dispatchChange(false, uri); 364 } else { 365 mChannelDataManager.getContentObserver().dispatchChange(false, uri); 366 } 367 } 368 } 369 370 // This implements the minimal methods in content resolver 371 // and detailed assumptions are written in each method. 372 private class FakeContentProvider extends MockContentProvider { 373 private final SparseArray<ChannelInfoWrapper> mChannelInfoList = new SparseArray<>(); 374 FakeContentProvider(Context context)375 public FakeContentProvider(Context context) { 376 super(context); 377 for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) { 378 mChannelInfoList.put(i, 379 new ChannelInfoWrapper(ChannelInfo.create(getContext(), i))); 380 } 381 } 382 383 /** 384 * Implementation of {@link ContentProvider#query}. 385 * This assumes that {@link ChannelDataManager} queries channels 386 * with empty {@code selection}. (i.e. channels are always queries for all) 387 */ 388 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)389 public Cursor query(Uri uri, String[] projection, String selection, String[] 390 selectionArgs, String sortOrder) { 391 if (DEBUG) { 392 Log.d(TAG, "dump query"); 393 Log.d(TAG, " uri=" + uri); 394 Log.d(TAG, " projection=" + Arrays.toString(projection)); 395 Log.d(TAG, " selection=" + selection); 396 } 397 assertChannelUri(uri); 398 return new FakeCursor(projection); 399 } 400 401 /** 402 * Implementation of {@link ContentProvider#update}. 403 * This assumes that {@link ChannelDataManager} update channels 404 * only for changing browsable and locked. 405 */ 406 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)407 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 408 if (DEBUG) Log.d(TAG, "update(uri=" + uri + ", selection=" + selection); 409 assertChannelUri(uri); 410 List<Long> channelIds = new ArrayList<>(); 411 try { 412 long channelId = ContentUris.parseId(uri); 413 channelIds.add(channelId); 414 } catch (NumberFormatException e) { 415 // Update for multiple channels. 416 if (TextUtils.isEmpty(selection)) { 417 for (int i = 0; i < mChannelInfoList.size(); i++) { 418 channelIds.add((long) mChannelInfoList.keyAt(i)); 419 } 420 } else { 421 // See {@link Utils#buildSelectionForIds} for the syntax. 422 String selectionForId = selection.substring( 423 selection.indexOf("(") + 1, selection.lastIndexOf(")")); 424 String[] ids = selectionForId.split(", "); 425 if (ids != null) { 426 for (String id : ids) { 427 channelIds.add(Long.parseLong(id)); 428 } 429 } 430 } 431 } 432 int updateCount = 0; 433 for (long channelId : channelIds) { 434 boolean updated = false; 435 ChannelInfoWrapper channel = mChannelInfoList.get((int) channelId); 436 if (channel == null) { 437 return 0; 438 } 439 if (values.containsKey(COLUMN_BROWSABLE)) { 440 updated = true; 441 channel.browsable = (values.getAsInteger(COLUMN_BROWSABLE) == 1); 442 } 443 if (values.containsKey(COLUMN_LOCKED)) { 444 updated = true; 445 channel.locked = (values.getAsInteger(COLUMN_LOCKED) == 1); 446 } 447 updateCount += updated ? 1 : 0; 448 } 449 if (updateCount > 0) { 450 if (channelIds.size() == 1) { 451 mContentResolver.notifyChange(uri, null); 452 } else { 453 mContentResolver.notifyChange(Channels.CONTENT_URI, null); 454 } 455 } else { 456 if (DEBUG) { 457 Log.d(TAG, "Update to channel(uri=" + uri + ") is ignored for " + values); 458 } 459 } 460 return updateCount; 461 } 462 463 /** 464 * Simulates channel data insert. 465 * This assigns original network ID (the same with channel number) to channel ID. 466 */ simulateInsert(ChannelInfo testChannelInfo)467 public void simulateInsert(ChannelInfo testChannelInfo) { 468 long channelId = testChannelInfo.originalNetworkId; 469 mChannelInfoList.put((int) channelId, 470 new ChannelInfoWrapper(ChannelInfo.create(getContext(), (int) channelId))); 471 mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null); 472 } 473 474 /** 475 * Simulates channel data delete. 476 */ simulateDelete(long channelId)477 public void simulateDelete(long channelId) { 478 mChannelInfoList.remove((int) channelId); 479 mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null); 480 } 481 482 /** 483 * Simulates channel data update. 484 */ simulateUpdate(long channelId, String newName)485 public void simulateUpdate(long channelId, String newName) { 486 ChannelInfoWrapper channel = mChannelInfoList.get((int) channelId); 487 ChannelInfo.Builder builder = new ChannelInfo.Builder(channel.channelInfo); 488 builder.setName(newName); 489 channel.channelInfo = builder.build(); 490 mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null); 491 } 492 assertChannelUri(Uri uri)493 private void assertChannelUri(Uri uri) { 494 assertTrue("Uri(" + uri + ") isn't channel uri", 495 uri.toString().startsWith(Channels.CONTENT_URI.toString())); 496 } 497 clear()498 public void clear() { 499 mChannelInfoList.clear(); 500 } 501 get(int position)502 public ChannelInfoWrapper get(int position) { 503 return mChannelInfoList.get(mChannelInfoList.keyAt(position)); 504 } 505 getCount()506 public int getCount() { 507 return mChannelInfoList.size(); 508 } 509 keyAt(int position)510 public long keyAt(int position) { 511 return mChannelInfoList.keyAt(position); 512 } 513 } 514 515 private class FakeCursor extends MockCursor { 516 private final String[] ALL_COLUMNS = { 517 Channels._ID, 518 Channels.COLUMN_DISPLAY_NAME, 519 Channels.COLUMN_DISPLAY_NUMBER, 520 Channels.COLUMN_INPUT_ID, 521 Channels.COLUMN_VIDEO_FORMAT, 522 Channels.COLUMN_ORIGINAL_NETWORK_ID, 523 COLUMN_BROWSABLE, 524 COLUMN_LOCKED}; 525 private final String[] mColumns; 526 private int mPosition; 527 FakeCursor(String[] columns)528 public FakeCursor(String[] columns) { 529 mColumns = (columns == null) ? ALL_COLUMNS : columns; 530 mPosition = -1; 531 } 532 533 @Override getColumnName(int columnIndex)534 public String getColumnName(int columnIndex) { 535 return mColumns[columnIndex]; 536 } 537 538 @Override getColumnIndex(String columnName)539 public int getColumnIndex(String columnName) { 540 for (int i = 0; i < mColumns.length; i++) { 541 if (mColumns[i].equalsIgnoreCase(columnName)) { 542 return i; 543 } 544 } 545 return -1; 546 } 547 548 @Override getLong(int columnIndex)549 public long getLong(int columnIndex) { 550 String columnName = getColumnName(columnIndex); 551 switch (columnName) { 552 case Channels._ID: 553 return mContentProvider.keyAt(mPosition); 554 } 555 if (DEBUG) { 556 Log.d(TAG, "Column (" + columnName + ") is ignored in getLong()"); 557 } 558 return 0; 559 } 560 561 @Override getString(int columnIndex)562 public String getString(int columnIndex) { 563 String columnName = getColumnName(columnIndex); 564 ChannelInfoWrapper channel = mContentProvider.get(mPosition); 565 switch (columnName) { 566 case Channels.COLUMN_DISPLAY_NAME: 567 return channel.channelInfo.name; 568 case Channels.COLUMN_DISPLAY_NUMBER: 569 return channel.channelInfo.number; 570 case Channels.COLUMN_INPUT_ID: 571 return DUMMY_INPUT_ID; 572 case Channels.COLUMN_VIDEO_FORMAT: 573 return channel.channelInfo.getVideoFormat(); 574 } 575 if (DEBUG) { 576 Log.d(TAG, "Column (" + columnName + ") is ignored in getString()"); 577 } 578 return null; 579 } 580 581 @Override getInt(int columnIndex)582 public int getInt(int columnIndex) { 583 String columnName = getColumnName(columnIndex); 584 ChannelInfoWrapper channel = mContentProvider.get(mPosition); 585 switch (columnName) { 586 case Channels.COLUMN_ORIGINAL_NETWORK_ID: 587 return channel.channelInfo.originalNetworkId; 588 case COLUMN_BROWSABLE: 589 return channel.browsable ? 1 : 0; 590 case COLUMN_LOCKED: 591 return channel.locked ? 1 : 0; 592 } 593 if (DEBUG) { 594 Log.d(TAG, "Column (" + columnName + ") is ignored in getInt()"); 595 } 596 return 0; 597 } 598 599 @Override getCount()600 public int getCount() { 601 return mContentProvider.getCount(); 602 } 603 604 @Override moveToNext()605 public boolean moveToNext() { 606 return ++mPosition < mContentProvider.getCount(); 607 } 608 609 @Override close()610 public void close() { 611 // No-op. 612 } 613 } 614 615 private class TestChannelDataManagerListener implements ChannelDataManager.Listener { 616 public CountDownLatch loadFinishedLatch = new CountDownLatch(1); 617 public CountDownLatch channelListUpdatedLatch = new CountDownLatch(1); 618 public boolean channelBrowsableChangedCalled; 619 620 @Override onLoadFinished()621 public void onLoadFinished() { 622 loadFinishedLatch.countDown(); 623 } 624 625 @Override onChannelListUpdated()626 public void onChannelListUpdated() { 627 channelListUpdatedLatch.countDown(); 628 } 629 630 @Override onChannelBrowsableChanged()631 public void onChannelBrowsableChanged() { 632 channelBrowsableChangedCalled = true; 633 } 634 reset()635 public void reset() { 636 loadFinishedLatch = new CountDownLatch(1); 637 channelListUpdatedLatch = new CountDownLatch(1); 638 channelBrowsableChangedCalled = false; 639 } 640 } 641 642 private class TestChannelDataManagerChannelListener 643 implements ChannelDataManager.ChannelListener { 644 public CountDownLatch channelChangedLatch = new CountDownLatch(1); 645 public final List<Channel> removedChannels = new ArrayList<>(); 646 public final List<Channel> updatedChannels = new ArrayList<>(); 647 648 @Override onChannelRemoved(Channel channel)649 public void onChannelRemoved(Channel channel) { 650 removedChannels.add(channel); 651 channelChangedLatch.countDown(); 652 } 653 654 @Override onChannelUpdated(Channel channel)655 public void onChannelUpdated(Channel channel) { 656 updatedChannels.add(channel); 657 channelChangedLatch.countDown(); 658 } 659 reset()660 public void reset() { 661 channelChangedLatch = new CountDownLatch(1); 662 removedChannels.clear(); 663 updatedChannels.clear(); 664 } 665 } 666 } 667